# HG changeset patch
# User Jonathan Kew <jfkthame@gmail.com>
# Date 1251640524 -3600
# Node ID f5770eb08c1366264f90aab1753f0ab128b90c5d
# Parent  aa3a30f3f1e8a34c2ccb39712a704f8385a04cbe
implement support for WOFF fonts via @font-face

diff --git a/gfx/thebes/public/gfxFT2Fonts.h b/gfx/thebes/public/gfxFT2Fonts.h
--- a/gfx/thebes/public/gfxFT2Fonts.h
+++ b/gfx/thebes/public/gfxFT2Fonts.h
@@ -83,29 +83,30 @@ public:
     FontEntry(const FontEntry& aFontEntry);
     ~FontEntry();
 
     const nsString& GetName() const {
         return mFaceName;
     }
 
     static FontEntry* 
-    CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, nsISupports *aLoader,
-                    const PRUint8 *aFontData, PRUint32 aLength);
+    CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
+                    nsTArray<PRUint8>& aFontData);
     
     static FontEntry* 
     CreateFontEntryFromFace(FT_Face aFace);
     
     cairo_font_face_t *CairoFontFace();
 
     cairo_font_face_t *mFontFace;
 
     nsString mFaceName;
     nsCString mFilename;
     PRUint8 mFTFontIndex;
+    nsTArray<PRUint8> mFontData;
 };
 
 
 
 class gfxFT2Font : public gfxFont {
 public: // new functions
     gfxFT2Font(FontEntry *aFontEntry,
                const gfxFontStyle *aFontStyle);
diff --git a/gfx/thebes/public/gfxFontUtils.h b/gfx/thebes/public/gfxFontUtils.h
--- a/gfx/thebes/public/gfxFontUtils.h
+++ b/gfx/thebes/public/gfxFontUtils.h
@@ -354,16 +354,23 @@ struct AutoSwap_PRUint64 {
 // used for overlaying name changes without touching original font data
 struct FontDataOverlay {
     // overlaySrc != 0 ==> use overlay
     PRUint32  overlaySrc;    // src offset from start of font data
     PRUint32  overlaySrcLen; // src length
     PRUint32  overlayDest;   // dest offset from start of font data
 };
     
+enum gfxUserFontType {
+    GFX_USERFONT_UNKNOWN = 0,
+    GFX_USERFONT_OPENTYPE = 1,
+    GFX_USERFONT_SVG = 2,
+    GFX_USERFONT_WOFF = 3
+};
+
 class THEBES_API gfxFontUtils {
 
 public:
     // these are public because gfxFont.cpp also looks into the name table
     enum {
         NAME_ID_FAMILY = 1,
         NAME_ID_STYLE = 2,
         NAME_ID_UNIQUE = 3,
@@ -481,16 +488,20 @@ public:
     // effectively hide existing fonts with matching names aHeaderLen is
     // the size of the header buffer on input, the actual size of the
     // EOT header on output
     static nsresult
     MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
                   nsTArray<PRUint8> *aHeader, FontDataOverlay *aOverlay);
 #endif
 
+    // determine the format of font data
+    static gfxUserFontType
+    DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength);
+    
     // checks for valid SFNT table structure, returns true if valid
     // does *not* guarantee that all font data is valid
     static PRBool
     ValidateSFNTHeaders(const PRUint8 *aFontData, PRUint32 aFontDataLength,
                         PRBool *aIsCFF = nsnull);
     
     // create a new name table and build a new font with that name table
     // appended on the end, returns true on success
diff --git a/gfx/thebes/public/gfxPangoFonts.h b/gfx/thebes/public/gfxPangoFonts.h
--- a/gfx/thebes/public/gfxPangoFonts.h
+++ b/gfx/thebes/public/gfxPangoFonts.h
@@ -83,19 +83,17 @@ public:
 
     static void Shutdown();
 
     // Used for @font-face { src: local(); }
     static gfxFontEntry *NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
                                       const nsAString &aFullname);
     // Used for @font-face { src: url(); }
     static gfxFontEntry *NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                                      nsISupports *aLoader,
-                                      const PRUint8 *aFontData,
-                                      PRUint32 aLength);
+                                      nsTArray<PRUint8>& aFontData);
 
     // Interfaces used internally
     // (but public so that they can be accessed from non-member functions):
 
     // The FontGroup holds the reference to the PangoFont (through the FontSet).
     PangoFont *GetBasePangoFont();
 
     // A language guessed from the gfxFontStyle
diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h
--- a/gfx/thebes/public/gfxPlatform.h
+++ b/gfx/thebes/public/gfxPlatform.h
@@ -206,24 +206,26 @@ public:
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName)
     { return nsnull; }
 
     /**
      * Activate a platform font.  (Needed to support @font-face src url().)
-     * aFontData must persist as long as a reference is held to aLoader.
+     * The newly created gfxFontEntry becomes responsible for keeping the data
+     * available as long as needed by the platform.
+     * It may use nsTArray<>::SwapElements to take over ownership of the buffer,
+     * rather than making a copy of the data; therefore, the caller must assume
+     * that aFontData is no longer valid after calling MakePlatformFont().
      * Ownership of the returned gfxFontEntry is passed to the caller,
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
-                                           const PRUint8 *aFontData,
-                                           PRUint32 aLength)
+                                           nsTArray<PRUint8>& aFontData)
     { return nsnull; }
 
     /**
      * Whether to allow downloadable fonts via @font-face rules
      */
     virtual PRBool DownloadableFontsEnabled();
 
     // check whether format is supported on a platform or not (if unclear, returns true)
diff --git a/gfx/thebes/public/gfxPlatformGtk.h b/gfx/thebes/public/gfxPlatformGtk.h
--- a/gfx/thebes/public/gfxPlatformGtk.h
+++ b/gfx/thebes/public/gfxPlatformGtk.h
@@ -97,19 +97,17 @@ public:
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      *
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
-                                           const PRUint8 *aFontData,
-                                           PRUint32 aLength);
+                                           nsTArray<PRUint8>& aFontData);
 
     /**
      * Check whether format is supported on a platform or not (if unclear,
      * returns true).
      */
     virtual PRBool IsFontFormatSupported(nsIURI *aFontURI,
                                          PRUint32 aFormatFlags);
 #endif
diff --git a/gfx/thebes/public/gfxPlatformMac.h b/gfx/thebes/public/gfxPlatformMac.h
--- a/gfx/thebes/public/gfxPlatformMac.h
+++ b/gfx/thebes/public/gfxPlatformMac.h
@@ -73,19 +73,17 @@ public:
                                   gfxUserFontSet *aUserFontSet);
 
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     virtual gfxPlatformFontList* CreatePlatformFontList();
 
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
-                                           const PRUint8 *aFontData,
-                                           PRUint32 aLength);
+                                           nsTArray<PRUint8>& aFontData);
 
     PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
 
     nsresult GetFontList(const nsACString& aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
     nsresult UpdateFontList();
 
diff --git a/gfx/thebes/public/gfxUserFontSet.h b/gfx/thebes/public/gfxUserFontSet.h
--- a/gfx/thebes/public/gfxUserFontSet.h
+++ b/gfx/thebes/public/gfxUserFontSet.h
@@ -43,16 +43,17 @@
 #include "gfxFontUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsIFile.h"
 
 class nsIURI;
+class nsIStreamLoader;
 class gfxMixedFontFamily;
 
 // parsed CSS @font-face rule information
 // lifetime: from when @font-face rule processed until font is loaded
 struct gfxFontFaceSrc {
     PRPackedBool           mIsLocal;       // url or local
 
     // if url, whether to use the origin principal or not
@@ -188,22 +189,21 @@ public:
                                 const gfxFontStyle& aFontStyle, 
                                 PRBool& aNeedsBold);
                                 
     // initialize the process that loads external font data, which upon 
     // completion will call OnLoadComplete method
     virtual nsresult StartLoad(gfxFontEntry *aFontToLoad, 
                                const gfxFontFaceSrc *aFontFaceSrc) = 0;
 
-    // when download has been completed, pass back data here
+    // when download has been completed, call back to the font set here
     // aDownloadStatus == NS_OK ==> download succeeded, error otherwise
     // returns true if platform font creation sucessful (or local()
     // reference was next in line)
-    PRBool OnLoadComplete(gfxFontEntry *aFontToLoad, nsISupports *aLoader,
-                          const PRUint8 *aFontData, PRUint32 aLength,
+    PRBool OnLoadComplete(gfxFontEntry *aFontToLoad, nsIStreamLoader *aLoader,
                           nsresult aDownloadStatus);
 
     // generation - each time a face is loaded, generation is
     // incremented so that the change can be recognized 
     PRUint64 GetGeneration() { return mGeneration; }
 
 protected:
     // for a given proxy font entry, attempt to load the next resource
diff --git a/gfx/thebes/public/gfxWindowsFonts.h b/gfx/thebes/public/gfxWindowsFonts.h
--- a/gfx/thebes/public/gfxWindowsFonts.h
+++ b/gfx/thebes/public/gfxWindowsFonts.h
@@ -135,19 +135,17 @@ public:
         mUnicodeRanges(aFontEntry.mUnicodeRanges)
     {
 
     }
     static void InitializeFontEmbeddingProcs();
 
     // create a font entry from downloaded font data
     static FontEntry* LoadFont(const gfxProxyFontEntry &aProxyEntry,
-                               nsISupports *aLoader,
-                               const PRUint8 *aFontData,
-                               PRUint32 aLength);
+                               nsTArray<PRUint8>& aFontData);
 
     // create a font entry for a font with a given name
     static FontEntry* CreateFontEntry(const nsAString& aName, 
                                       gfxWindowsFontType aFontType, 
                                       PRBool aItalic, PRUint16 aWeight, 
                                       gfxUserFontData* aUserFontData, 
                                       HDC hdc = 0, LOGFONTW *aLogFont = nsnull);
 
diff --git a/gfx/thebes/public/gfxWindowsPlatform.h b/gfx/thebes/public/gfxWindowsPlatform.h
--- a/gfx/thebes/public/gfxWindowsPlatform.h
+++ b/gfx/thebes/public/gfxWindowsPlatform.h
@@ -121,19 +121,17 @@ public:
      */
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
-                                           const PRUint8 *aFontData,
-                                           PRUint32 aLength);
+                                           nsTArray<PRUint8>& aFontData);
 
     /**
      * Check whether format is supported on a platform or not (if unclear, returns true)
      */
     virtual PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
 
     /* Given a string and a font we already have find the font that
      * supports the most code points and most closely resembles aFont
diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in
--- a/gfx/thebes/src/Makefile.in
+++ b/gfx/thebes/src/Makefile.in
@@ -149,16 +149,18 @@ endif
 endif
 
 CMMSRCS = gfxMacPlatformFontList.mm
 
 # Always link with OpenGL/AGL
 EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL -framework QuickTime
 endif
 
+CSRCS += woff.c
+
 EXTRA_DSO_LDOPTS += $(TK_LIBS)
 
 DEFINES += -DIMPL_THEBES
 
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
 CFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
diff --git a/gfx/thebes/src/gfxFT2Fonts.cpp b/gfx/thebes/src/gfxFT2Fonts.cpp
--- a/gfx/thebes/src/gfxFT2Fonts.cpp
+++ b/gfx/thebes/src/gfxFT2Fonts.cpp
@@ -75,32 +75,38 @@ FontEntry::~FontEntry()
         cairo_font_face_destroy(mFontFace);
         mFontFace = nsnull;
     }
 }
 
 /* static */
 FontEntry*  
 FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, 
-                           nsISupports *aLoader, const PRUint8 *aFontData, 
-                           PRUint32 aLength) {
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength))
+                           nsTArray<PRUint8>& aFontData) {
+    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData.Elements(),
+                                           aFontData.Length())) {
         return nsnull;
+    }
     FT_Face face;
     FT_Error error =
         FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), 
-                           aFontData, aLength, 0, &face);
+                           aFontData.Elements(), aFontData.Length(), 0, &face);
     if (error != FT_Err_Ok)
         return nsnull;
+
     FontEntry* fe = FontEntry::CreateFontEntryFromFace(face);
     fe->mItalic = aProxyEntry.mItalic;
     fe->mWeight = aProxyEntry.mWeight;
     fe->mStretch = aProxyEntry.mStretch;
+
+    // give ownership of the font data buffer to the fontEntry,
+    // so it won't disappear when the loader is destroyed
+    fe->mFontData.SwapElements(aFontData);
+
     return fe;
-
 }
 
 static void
 FTFontDestroyFunc(void *data)
 {
     FT_Face face = (FT_Face)data;
     FT_Done_Face(face);
 }
diff --git a/gfx/thebes/src/gfxFontUtils.cpp b/gfx/thebes/src/gfxFontUtils.cpp
--- a/gfx/thebes/src/gfxFontUtils.cpp
+++ b/gfx/thebes/src/gfxFontUtils.cpp
@@ -47,16 +47,18 @@
 #include "nsISupportsPrimitives.h"
 #include "nsIStreamBufferAccess.h"
 #include "nsIUUIDGenerator.h"
 #include "nsMemory.h"
 #include "nsICharsetConverterManager.h"
 
 #include "plbase64.h"
 
+#include "woff.h"
+
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
 #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
 
 /* Unicode subrange table
  *   from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_63ub.asp
@@ -689,16 +691,43 @@ ValidateKernTable(const PRUint8 *aKernTa
         // for now, assume this is OK
         return PR_TRUE;
     }
 
     // neither the old Windows version nor the newer Apple one; refuse to use it
     return PR_FALSE;
 }
 
+gfxUserFontType
+gfxFontUtils::DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength)
+{
+    // test for OpenType font data
+    // problem: EOT-Lite with 0x10000 length will look like TrueType!
+    if (aFontDataLength >= sizeof(SFNTHeader)) {
+        const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+        PRUint32 sfntVersion = sfntHeader->sfntVersion;
+        if (IsValidSFNTVersion(sfntVersion)) {
+            return GFX_USERFONT_OPENTYPE;
+        }
+    }
+    
+    // test for WOFF
+    if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
+        const AutoSwap_PRUint32 *version = 
+            reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
+        if (PRUint32(*version) == TRUETYPE_TAG('w','O','F','F')) {
+            return GFX_USERFONT_WOFF;
+        }
+    }
+    
+    // tests for other formats here
+    
+    return GFX_USERFONT_UNKNOWN;
+}
+
 PRBool
 gfxFontUtils::ValidateSFNTHeaders(const PRUint8 *aFontData, 
                                   PRUint32 aFontDataLength, 
                                   PRBool *aIsCFF)
 {
     NS_ASSERTION(aFontData && aFontDataLength != 0, "null font data");
 
     PRUint64 dataLength(aFontDataLength);
diff --git a/gfx/thebes/src/gfxMacPlatformFontList.h b/gfx/thebes/src/gfxMacPlatformFontList.h
--- a/gfx/thebes/src/gfxMacPlatformFontList.h
+++ b/gfx/thebes/src/gfxMacPlatformFontList.h
@@ -93,17 +93,17 @@ public:
     virtual gfxFontEntry* GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold);
 
     virtual PRBool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
     
     virtual gfxFontEntry* MakePlatformFont(const gfxFontEntry *aProxyEntry,
-                                           const PRUint8 *aFontData, PRUint32 aLength);
+                                           nsTArray<PRUint8>& aFontData);
 
     void ClearPrefFonts() { mPrefFonts.Clear(); }
 
 private:
     friend class gfxPlatformMac;
 
     gfxMacPlatformFontList();
 
diff --git a/gfx/thebes/src/gfxMacPlatformFontList.mm b/gfx/thebes/src/gfxMacPlatformFontList.mm
--- a/gfx/thebes/src/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/src/gfxMacPlatformFontList.mm
@@ -686,26 +686,27 @@ public:
             ::ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
     }
 
     ATSFontContainerRef     mContainerRef;
 };
 
 gfxFontEntry* 
 gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry, 
-                                     const PRUint8 *aFontData, PRUint32 aLength)
+                                         nsTArray<PRUint8>& aFontData)
 {
     OSStatus err;
     
-    NS_ASSERTION(aFontData && aLength != 0, 
-                 "MakePlatformFont called with null data ptr");
+    NS_ASSERTION(aFontData.Length() > 0,
+                 "MakePlatformFont called with no data");
                  
     // do simple validation check on font data before 
     // attempting to activate it
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
+    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData.Elements(),
+                                           aFontData.Length())) {
 #if DEBUG
         char warnBuf[1024];
         const gfxProxyFontEntry *proxyEntry = 
             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
         sprintf(warnBuf, "downloaded font error, invalid font data for (%s)",
                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
         NS_WARNING(warnBuf);
 #endif    
@@ -715,17 +716,18 @@ gfxMacPlatformFontList::MakePlatformFont
     ATSFontRef fontRef;
     ATSFontContainerRef containerRef;
 
     // we get occasional failures when multiple fonts are activated in quick succession
     // if the ATS font cache is damaged; to work around this, we can retry the activation
     const PRUint32 kMaxRetries = 3;
     PRUint32 retryCount = 0;
     while (retryCount++ < kMaxRetries) {
-        err = ::ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength, 
+        err = ::ATSFontActivateFromMemory(aFontData.Elements(),
+                                          aFontData.Length(), 
                                           kPrivateATSFontContextPrivate,
                                           kATSFontFormatUnspecified,
                                           NULL, 
                                           kATSOptionFlagsDoNotNotify, 
                                           &containerRef);
         mATSGeneration = ::ATSGetGeneration();
 
         if (err != noErr) {
diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -295,42 +295,45 @@ public:
 /**
  * gfxDownloadedFcFontEntry:
  *
  * An implementation of gfxFcFontEntry for web fonts from src:url().
  */
 
 class gfxDownloadedFcFontEntry : public gfxFcFontEntry {
 public:
-    // This takes ownership of the face.
+    // This takes ownership of the face, and the font data it depends on
+    // (by using nsTArray.SwapElements() to adopt the buffer)
     gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                             nsISupports *aLoader, FT_Face aFace)
-        : gfxFcFontEntry(aProxyEntry), mLoader(aLoader), mFace(aFace)
+                             nsTArray<PRUint8>& aFontData, FT_Face aFace)
+        : gfxFcFontEntry(aProxyEntry), mFace(aFace)
     {
         NS_PRECONDITION(aFace != NULL, "aFace is NULL!");
         InitPattern();
+        mFontData.SwapElements(aFontData);
     }
 
     virtual ~gfxDownloadedFcFontEntry();
 
     // Returns a PangoCoverage owned by the FontEntry.  The caller must add a
     // reference if it wishes to keep the PangoCoverage longer than the
     // lifetime of the FontEntry.
     PangoCoverage *GetPangoCoverage();
 
 protected:
     virtual void InitPattern();
 
-    // mLoader holds a reference to memory used by mFace.
-    nsCOMPtr<nsISupports> mLoader;
     FT_Face mFace;
     // mPangoCoverage is the charset property of the pattern translated to a
     // format that Pango understands.  A reference is kept here so that it can
     // be shared by multiple PangoFonts (of different sizes).
     nsAutoRef<PangoCoverage> mPangoCoverage;
+    // mFontData holds the data used to instantiate the FT_Face;
+    // this has to persist until we are finished with the face.
+    nsTArray<PRUint8> mFontData;
 };
 
 // A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
 static const char *kFontEntryFcProp = "-moz-font-entry";
 
 static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
                                      gfxDownloadedFcFontEntry *aFontEntry)
 {
@@ -2213,28 +2216,29 @@ GetFTLibrary()
         gFTLibrary = face.get()->glyph->library;
     }
 
     return gFTLibrary;
 }
 
 /* static */ gfxFontEntry *
 gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                                nsISupports *aLoader,
-                                const PRUint8 *aFontData, PRUint32 aLength)
+                                nsTArray<PRUint8>& aFontData)
 {
     // Using face_index = 0 for the first face in the font, as we have no
     // other information.  FT_New_Memory_Face checks for a NULL FT_Library.
     FT_Face face;
     FT_Error error =
-        FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
+        FT_New_Memory_Face(GetFTLibrary(),
+                           aFontData.Elements(), aFontData.Length(),
+                           0, &face);
     if (error != 0)
         return nsnull;
 
-    return new gfxDownloadedFcFontEntry(aProxyEntry, aLoader, face);
+    return new gfxDownloadedFcFontEntry(aProxyEntry, aFontData, face);
 }
 
 
 static double
 GetPixelSize(FcPattern *aPattern)
 {
     double size;
     if (FcPatternGetDouble(aPattern,
diff --git a/gfx/thebes/src/gfxPlatformFontList.h b/gfx/thebes/src/gfxPlatformFontList.h
--- a/gfx/thebes/src/gfxPlatformFontList.h
+++ b/gfx/thebes/src/gfxPlatformFontList.h
@@ -104,18 +104,17 @@ public:
                                          PRBool& aNeedsBold) = 0;
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName) = 0;
 
     // create a new platform font from downloaded data (@font-face)
     virtual gfxFontEntry* MakePlatformFont(const gfxFontEntry *aProxyEntry,
-                                           const PRUint8 *aFontData,
-                                           PRUint32 aLength) = 0;
+                                           nsTArray<PRUint8>& aFontData) = 0;
 
     // get the standard family name on the platform for a given font name
     virtual PRBool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) = 0;
 
 protected:
     gfxPlatformFontList();
 
     static gfxPlatformFontList *sPlatformFontList;
diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp
--- a/gfx/thebes/src/gfxPlatformGtk.cpp
+++ b/gfx/thebes/src/gfxPlatformGtk.cpp
@@ -292,26 +292,24 @@ gfxPlatformGtk::LookupLocalFont(const gf
 gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFontName)
 {
     return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontName);
 }
 
 gfxFontEntry* 
 gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, 
-                                 nsISupports *aLoader,
-                                 const PRUint8 *aFontData, PRUint32 aLength)
+                                 nsTArray<PRUint8>& aFontData)
 {
     // Just being consistent with other platforms.
     // This will mean that only fonts in SFNT formats will be accepted.
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength))
+    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData.Elements(), aFontData.Length()))
         return nsnull;
 
-    return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aLoader,
-                                           aFontData, aLength);
+    return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontData);
 }
 
 PRBool
 gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
                  "strange font format hint set");
diff --git a/gfx/thebes/src/gfxPlatformMac.cpp b/gfx/thebes/src/gfxPlatformMac.cpp
--- a/gfx/thebes/src/gfxPlatformMac.cpp
+++ b/gfx/thebes/src/gfxPlatformMac.cpp
@@ -182,20 +182,19 @@ gfxPlatformMac::LookupLocalFont(const gf
                                 const nsAString& aFontName)
 {
     return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aProxyEntry, 
                                                                     aFontName);
 }
 
 gfxFontEntry* 
 gfxPlatformMac::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                 nsISupports *aLoader,
-                                 const PRUint8 *aFontData, PRUint32 aLength)
+                                 nsTArray<PRUint8>& aFontData)
 {
-    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, aFontData, aLength);
+    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, aFontData);
 }
 
 PRBool
 gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
                  "strange font format hint set");
diff --git a/gfx/thebes/src/gfxUserFontSet.cpp b/gfx/thebes/src/gfxUserFontSet.cpp
--- a/gfx/thebes/src/gfxUserFontSet.cpp
+++ b/gfx/thebes/src/gfxUserFontSet.cpp
@@ -10,21 +10,22 @@
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is thebes gfx code.
  *
  * The Initial Developer of the Original Code is Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * Portions created by the Initial Developer are Copyright (C) 2008-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   John Daggett <jdaggett@mozilla.com>
+ *   Jonathan Kew <jfkthame@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -40,16 +41,19 @@
 #endif /* MOZ_LOGGING */
 #include "prlog.h"
 
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "prlong.h"
+#include "nsIStreamLoader.h"
+
+#include "woff.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gUserFontsLog = PR_NewLogModule("userfonts");
 #endif /* PR_LOGGING */
 
 #define LOG(args) PR_LOG(gUserFontsLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gUserFontsLog, PR_LOG_DEBUG)
 
@@ -82,17 +86,16 @@ gfxUserFontSet::gfxUserFontSet()
 gfxUserFontSet::gfxUserFontSet()
 {
     mFontFamilies.Init(5);
     IncrementGeneration();
 }
 
 gfxUserFontSet::~gfxUserFontSet()
 {
-
 }
 
 void
 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, 
                             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 
                             PRUint32 aWeight, 
                             PRUint32 aStretch, 
                             PRUint32 aItalicStyle, 
@@ -164,35 +167,82 @@ gfxUserFontSet::FindFontEntry(const nsAS
     // search again
     if (status == STATUS_LOADED)
         return family->FindFontForStyle(aFontStyle, aNeedsBold);
 
     // if either loading or an error occurred, return null
     return nsnull;
 }
 
+PRBool
+PrepareOpenTypeData(nsTArray<PRUint8>& aData)
+{
+    switch(gfxFontUtils::DetermineFontDataType(aData.Elements(),
+                                               aData.Length())) {
+    
+    case GFX_USERFONT_OPENTYPE:
+        // nothing to do
+        return PR_TRUE;
+        
+    case GFX_USERFONT_WOFF: {
+        nsTArray<PRUint8> decodedData;
+        PRUint32 status = eWOFF_ok;
+        const PRUint8 * woffData = aData.Elements();
+        PRUint32 woffLength = aData.Length();
+        PRUint32 bufferSize = woffGetDecodedSize(woffData, woffLength,
+                                                 &status);
+        if (WOFF_FAILURE(status)) {
+            break;
+        }
+        decodedData.SetLength(bufferSize);
+        woffDecodeToBuffer(woffData, woffLength,
+                           decodedData.Elements(), bufferSize,
+                           nsnull, &status);
+        if (WOFF_SUCCESS(status)) {
+            aData.SwapElements(decodedData);
+            return PR_TRUE;
+        }
+        return PR_FALSE;
+    }
+        
+    // xxx - add support for other wrappers here
+    
+    default:
+        // should we have a warning here?
+        break;
+    }
+
+    return PR_FALSE;
+}
 
 PRBool 
 gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad,
-                               nsISupports *aLoader,
-                               const PRUint8 *aFontData, PRUint32 aLength, 
+                               nsIStreamLoader *aLoader,
                                nsresult aDownloadStatus)
 {
     NS_ASSERTION(aFontToLoad->mIsProxy, "trying to load font data for wrong font entry type");
 
     if (!aFontToLoad->mIsProxy)
         return PR_FALSE;
 
     gfxProxyFontEntry *pe = static_cast<gfxProxyFontEntry*> (aFontToLoad);
 
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
-        gfxFontEntry *fe = 
-            gfxPlatform::GetPlatform()->MakePlatformFont(pe, aLoader,
-                                                         aFontData, aLength);
+        gfxFontEntry *fe = nsnull;
+        nsTArray<PRUint8> fontData;
+
+        aLoader->ExtractData(&fontData); // takes over ownership from the loader
+
+        // unwrap/decompress or otherwise munge the downloaded data
+        // to make a usable sfnt structure, then activate it on the platform
+        if (PrepareOpenTypeData(fontData)) {
+            fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe, fontData);
+        }
+
         if (fe) {
             static_cast<gfxMixedFontFamily*>(pe->mFamily)->ReplaceFontEntry(pe, fe);
             IncrementGeneration();
 #ifdef PR_LOGGING
             if (LOG_ENABLED()) {
                 nsCAutoString fontURI;
                 pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
 
diff --git a/gfx/thebes/src/gfxWindowsFonts.cpp b/gfx/thebes/src/gfxWindowsFonts.cpp
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -510,24 +510,24 @@ public:
         return eotReader->Read(outBuffer, aBytesToRead);
     }        
         
 };
 
 /* static */
 FontEntry* 
 FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry, 
-                    nsISupports *aLoader,const PRUint8 *aFontData, 
-                    PRUint32 aLength) {
+                    nsTArray<PRUint8>& aFontData) {
     // if calls aren't available, bail
     if (!TTLoadEmbeddedFontPtr || !TTDeleteEmbeddedFontPtr)
         return nsnull;
 
     PRBool isCFF;
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength, &isCFF))
+    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData.Elements(),
+                                           aFontData.Length(), &isCFF))
         return nsnull;
         
     nsresult rv;
     HANDLE fontRef = nsnull;
     PRBool isEmbedded = PR_FALSE;
 
     nsAutoString uniqueName;
     rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
@@ -542,45 +542,47 @@ FontEntry::LoadFont(const gfxProxyFontEn
         PRUint32 eotlen;
 
         isEmbedded = PR_TRUE;
         PRUint32 nameLen = PR_MIN(uniqueName.Length(), LF_FACESIZE - 1);
         nsPromiseFlatString fontName(Substring(uniqueName, 0, nameLen));
         
         FontDataOverlay overlayNameData = {0, 0, 0};
 
-        rv = gfxFontUtils::MakeEOTHeader(aFontData, aLength, &eotHeader, 
-                                         &overlayNameData);
+        rv = gfxFontUtils::MakeEOTHeader(aFontData.Elements(), aFontData.Length(),
+                                         &eotHeader, &overlayNameData);
         if (NS_FAILED(rv))
             return nsnull;
 
         // load in embedded font data
         eotlen = eotHeader.Length();
         buffer = reinterpret_cast<PRUint8*> (eotHeader.Elements());
         
         PRInt32 ret;
         ULONG privStatus, pulStatus;
-        EOTFontStreamReader eotReader(aFontData, aLength, buffer, eotlen,
-                                      &overlayNameData);
+        EOTFontStreamReader eotReader(aFontData.Elements(), aFontData.Length(),
+                                      buffer, eotlen, &overlayNameData);
 
         ret = TTLoadEmbeddedFontPtr(&fontRef, TTLOAD_PRIVATE, &privStatus, 
                                    LICENSE_PREVIEWPRINT, &pulStatus, 
                                    EOTFontStreamReader::ReadEOTStream, 
                                    &eotReader, (PRUnichar*)(fontName.get()), 0, 0);
         if (ret != E_NONE)
             fontRef = nsnull;
     }
 
     // load CFF fonts or fonts that failed with t2embed loader
     if (fontRef == nsnull) {
         // Postscript-style glyphs, swizzle name table, load directly
         nsTArray<PRUint8> newFontData;
 
         isEmbedded = PR_FALSE;
-        rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
+        rv = gfxFontUtils::RenameFont(uniqueName,
+                                      aFontData.Elements(), aFontData.Length(),
+                                      &newFontData);
 
         if (NS_FAILED(rv))
             return nsnull;
         
         DWORD numFonts = 0;
 
         PRUint8 *fontData = reinterpret_cast<PRUint8*> (newFontData.Elements());
         PRUint32 fontLength = newFontData.Length();
diff --git a/gfx/thebes/src/gfxWindowsPlatform.cpp b/gfx/thebes/src/gfxWindowsPlatform.cpp
--- a/gfx/thebes/src/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/src/gfxWindowsPlatform.cpp
@@ -829,23 +829,22 @@ gfxWindowsPlatform::LookupLocalFont(cons
     return data.mFontEntry;
 #else
     return FontEntry::LoadLocalFont(*aProxyEntry, aFontName);
 #endif
 }
 
 gfxFontEntry* 
 gfxWindowsPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                     nsISupports *aLoader,
-                                     const PRUint8 *aFontData, PRUint32 aLength)
+                                     nsTArray<PRUint8>& aFontData)
 {
 #ifdef MOZ_FT2_FONTS
-    return FontEntry::CreateFontEntry(*aProxyEntry, aLoader, aFontData, aLength);
+    return FontEntry::CreateFontEntry(*aProxyEntry, aFontData);
 #else
-    return FontEntry::LoadFont(*aProxyEntry, aLoader, aFontData, aLength);
+    return FontEntry::LoadFont(*aProxyEntry, aFontData);
 #endif    
 }
 
 PRBool
 gfxWindowsPlatform::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
diff --git a/gfx/thebes/src/woff-private.h b/gfx/thebes/src/woff-private.h
new file mode 100755
--- /dev/null
+++ b/gfx/thebes/src/woff-private.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef WOFF_PRIVATE_H_
+#define WOFF_PRIVATE_H_
+
+#include "woff.h"
+
+/* private definitions used in the WOFF encoder/decoder functions */
+
+/* create an OT tag from 4 characters */
+#define TAG(a,b,c,d) ((a)<<24 | (b)<<16 | (c)<<8 | (d))
+
+#define WOFF_SIGNATURE    TAG('w','O','F','F')
+
+#define SFNT_VERSION_CFF  TAG('O','T','T','O')
+#define SFNT_VERSION_TT   0x00010000
+#define SFNT_VERSION_true TAG('t','r','u','e')
+
+#define TABLE_TAG_DSIG    TAG('D','S','I','G')
+#define TABLE_TAG_head    TAG('h','e','a','d')
+
+/* These macros to read values as big-endian only work on "real" variables,
+   not general expressions, because of the use of &(x), but they are
+   designed to work on both BE and LE machines without the need for a
+   configure check. For production code, we might want to replace this
+   with something more efficient. */
+/* read a 32-bit BigEndian value */
+#define READ32BE(x) ( ( (uint32_t) ((uint8_t*)&(x))[0] << 24 ) + \
+                      ( (uint32_t) ((uint8_t*)&(x))[1] << 16 ) + \
+                      ( (uint32_t) ((uint8_t*)&(x))[2] <<  8 ) + \
+                        (uint32_t) ((uint8_t*)&(x))[3]           )
+/* read a 16-bit BigEndian value */
+#define READ16BE(x) ( ( (uint16_t) ((uint8_t*)&(x))[0] << 8 ) + \
+                        (uint16_t) ((uint8_t*)&(x))[1]          )
+
+#pragma pack(push,1)
+
+typedef struct {
+  uint32_t version;
+  uint16_t numTables;
+  uint16_t searchRange;
+  uint16_t entrySelector;
+  uint16_t rangeShift;
+} sfntHeader;
+
+typedef struct {
+  uint32_t tag;
+  uint32_t checksum;
+  uint32_t offset;
+  uint32_t length;
+} sfntDirEntry;
+
+typedef struct {
+  uint32_t signature;
+  uint32_t flavor;
+  uint32_t length;
+  uint32_t numTables;
+  uint32_t totalOtfSize;
+  uint16_t majorVersion;
+  uint16_t minorVersion;
+  uint32_t metaOffset;
+  uint32_t metaCompLen;
+  uint32_t metaOrigLen;
+  uint32_t privOffset;
+  uint32_t privLen;
+} woffHeader;
+
+typedef struct {
+  uint32_t tag;
+  uint32_t offset;
+  uint32_t compLen;
+  uint32_t origLen;
+  uint32_t checksum;
+} woffDirEntry;
+
+#pragma pack(pop)
+
+#endif
diff --git a/gfx/thebes/src/woff.c b/gfx/thebes/src/woff.c
new file mode 100755
--- /dev/null
+++ b/gfx/thebes/src/woff.c
@@ -0,0 +1,893 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "woff-private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#pragma pack(push,1)
+
+typedef struct {
+  uint32_t version;
+  uint32_t fontRevision;
+  uint32_t checkSumAdjustment;
+  uint32_t magicNumber;
+  uint16_t flags;
+  uint16_t unitsPerEm;
+  uint32_t created[2];
+  uint32_t modified[2];
+  int16_t xMin;
+  int16_t yMin;
+  int16_t xMax;
+  int16_t yMax;
+  uint16_t macStyle;
+  uint16_t lowestRecPpem;
+  int16_t fontDirectionHint;
+  int16_t indexToLocFormat;
+  int16_t glyphDataFormat;
+} sfntHeadTable;
+
+#pragma pack(pop)
+
+const size_t HEAD_TABLE_SIZE = 54; /* sizeof(sfntHeadTable) may report 56
+                                      because of alignment */
+
+/*
+ * Just simple whole-file encoding and decoding functions; a real WOFF library
+ * may provide support for accessing individual tables from a compressed font,
+ * better options for memory allocation/ownership, and proper error handling.
+ * This is just a quick proof-of-concept, really.
+ */
+
+/* on errors, each function sets a status variable and jumps to failure: */
+#undef FAIL
+#define FAIL(err) do { status |= err; goto failure; } while (0)
+
+/******************************************************************/
+/* * * * * * * * * * * * * * ENCODING * * * * * * * * * * * * * * */
+/******************************************************************/
+
+const uint8_t *
+woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
+           uint16_t majorVersion, uint16_t minorVersion,
+           uint32_t * woffLen, uint32_t * pStatus)
+{
+  woffDirEntry * woffDir = NULL;
+  uint8_t * tableData = NULL;
+  uint8_t * woffData = NULL;
+
+  uint32_t numWoffTables = 0;
+  uint32_t tableDataLen = 0;
+  uint32_t totalOtfSize = sizeof(sfntHeader);
+
+  uint16_t numSfntTables;
+  const sfntDirEntry * sfntDir;
+  uint16_t i;
+  uint32_t tableBase;
+  uint32_t totalLength;
+  woffHeader * newHeader;
+
+  uint32_t status = eWOFF_ok;
+
+  const sfntHeader * header = (const sfntHeader *) (sfntData);
+  if (READ32BE(header->version) != SFNT_VERSION_TT &&
+      READ32BE(header->version) != SFNT_VERSION_CFF &&
+      READ32BE(header->version) != SFNT_VERSION_true) {
+    status |= eWOFF_warn_unknown_version;
+  }
+
+  numSfntTables = READ16BE(header->numTables);
+
+  sfntDir = (const sfntDirEntry *) (sfntData + sizeof(sfntHeader));
+
+  /* allocate space for building the directory;
+     this is a maximum, we might skip a DSIG table */
+  woffDir = (woffDirEntry *) (malloc(numSfntTables * sizeof(woffDirEntry)));
+  if (!woffDir) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  /* table data block will grow incrementally */
+  tableData = (uint8_t *) malloc(0);
+  if (!tableData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  for (i = 0; i < numSfntTables; ++i) {
+    uLong sourceLen, destLen;
+    uint32_t sourceOffset;
+
+    if (READ32BE(sfntDir[i].tag) == TABLE_TAG_DSIG) {
+      continue;
+    }
+
+    woffDir[numWoffTables].tag = sfntDir[i].tag;
+    woffDir[numWoffTables].checksum = sfntDir[i].checksum;
+    woffDir[numWoffTables].origLen = sfntDir[i].length;
+
+    /* allocate enough space for upper bound of compressed size */
+    sourceOffset = READ32BE(sfntDir[i].offset);
+    sourceLen = READ32BE(sfntDir[i].length);
+    if (sourceOffset + sourceLen > sfntLen) {
+      FAIL(eWOFF_invalid);
+    }
+    destLen = compressBound(sourceLen);
+    tableData = (uint8_t *) realloc(tableData, tableDataLen + destLen);
+    if (!tableData) {
+      FAIL(eWOFF_out_of_memory);
+    }
+
+    /* do the compression directly into the table data block */
+    if (compress2((Bytef *) (tableData + tableDataLen), &destLen,
+                  (const Bytef *) (sfntData + sourceOffset),
+                  sourceLen, 9) != Z_OK) {
+      FAIL(eWOFF_compression_failure);
+    }
+    if (destLen < sourceLen) {
+      /* compressed table was smaller */
+      /* record the offset from start of tableData (will be updated later) */
+      woffDir[numWoffTables].offset = tableDataLen;
+      tableDataLen += destLen;
+    } else {
+      /* compression didn't make it smaller,
+         so we store the original data instead */
+      /* ensure length is multiple of 4 */
+      destLen = (sourceLen + 3) & ~3;
+      /* reallocate to ensure enough space for the padded table,
+         plus any padding before it */
+      tableData = (uint8_t *) realloc(tableData,
+                                      ((tableDataLen + 3) & ~3) + destLen);
+      if (!tableData) {
+        FAIL(eWOFF_out_of_memory);
+      }
+      /* zero-fill pad bytes so the table starts on a 4-byte boundary */
+      while ((tableDataLen & 3) != 0) {
+        tableData[tableDataLen++] = 0;
+      }
+      /* record the offset from start of tableData (will be updated later) */
+      woffDir[numWoffTables].offset = tableDataLen;
+      /* copy the original data into place */
+      memcpy(tableData + tableDataLen,
+             sfntData + READ32BE(sfntDir[i].offset), sourceLen);
+      tableDataLen += sourceLen;
+      /* pad end of table if necessary to 4-byte boundary */
+      while (sourceLen < destLen) {
+        tableData[tableDataLen++] = 0;
+        sourceLen++;
+      }
+    }
+
+    /* record the compressed (or not) length we actually stored */
+    woffDir[numWoffTables].compLen = READ32BE(destLen);
+    ++numWoffTables;
+
+    /* update total size of uncompressed OpenType
+       with table size + dir entry size */
+    totalOtfSize += sizeof(sfntDirEntry);
+    totalOtfSize += (sourceLen + 3) & ~3;
+  }
+
+  /* now we know how big the directory will be, so update table offsets */
+  tableBase = sizeof(woffHeader) + numWoffTables * sizeof(woffDirEntry);
+  for (i = 0; i < numWoffTables; ++i) {
+    /* values were stored without byte-swapping earlier */
+    uint32_t offset = tableBase + woffDir[i].offset;
+    /* but now we need to make sure they are big-endian */
+    woffDir[i].offset = READ32BE(offset);
+  }
+
+  /* allocate space to build the complete WOFF file */
+  totalLength = sizeof(woffHeader) +
+                  numWoffTables * sizeof(woffDirEntry) + tableDataLen;
+  woffData = (uint8_t *) malloc(totalLength);
+  if (!woffData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  /* write the header */
+  newHeader = (woffHeader *) (woffData);
+  newHeader->signature = WOFF_SIGNATURE;
+  newHeader->signature = READ32BE(newHeader->signature);
+  newHeader->flavor = header->version;
+  newHeader->length = READ32BE(totalLength);
+  newHeader->numTables = READ32BE(numWoffTables);
+  newHeader->totalOtfSize = READ32BE(totalOtfSize);
+  newHeader->majorVersion = READ16BE(majorVersion);
+  newHeader->minorVersion = READ16BE(minorVersion);
+  newHeader->metaOffset = 0;
+  newHeader->metaCompLen = 0;
+  newHeader->metaOrigLen = 0;
+  newHeader->privOffset = 0;
+  newHeader->privLen = 0;
+
+  /* copy in the entries actually used in the directory */
+  memcpy(woffData + sizeof(woffHeader),
+         woffDir, numWoffTables * sizeof(woffDirEntry));
+  free(woffDir);
+
+  /* copy in the compressed table data */
+  memcpy(woffData + tableBase, tableData, tableDataLen);
+  free(tableData);
+  
+  if (woffLen) {
+    *woffLen = tableBase + tableDataLen;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return woffData;
+
+failure:
+  if (woffDir) {
+    free(woffDir);
+  }
+  if (tableData) {
+    free(tableData);
+  }
+  if (woffData) {
+    free(woffData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+static const uint8_t *
+rebuildWoff(const uint8_t * woffData, uint32_t * woffLen,
+            const uint8_t * metaData, uint32_t metaCompLen, uint32_t metaOrigLen,
+            const uint8_t * privData, uint32_t privLen, uint32_t * pStatus)
+{
+  const woffHeader * origHeader;
+  const woffDirEntry * origDir;
+  uint8_t * newData = NULL;
+  uint8_t * tableData = NULL;
+  woffHeader * newHeader;
+  woffDirEntry * newDir;
+  uint32_t tableBase, tableOffset, numTables;
+  uint32_t tableDataLen, totalLength;
+  int i;
+  uint32_t status = eWOFF_ok;
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  origHeader = (const woffHeader *) (woffData);
+
+  if (READ32BE(origHeader->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  numTables = READ32BE(origHeader->numTables);
+  origDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+  tableBase = sizeof(woffHeader) + numTables * sizeof(woffDirEntry);
+  newHeader = (woffHeader *) malloc(tableBase);
+  if (!newHeader) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  newHeader->signature = origHeader->signature;
+  newHeader->flavor = origHeader->flavor;
+  newHeader->length = 0;
+  newHeader->numTables = origHeader->numTables;
+  newHeader->totalOtfSize = origHeader->totalOtfSize;
+  newHeader->majorVersion = origHeader->majorVersion;
+  newHeader->minorVersion = origHeader->minorVersion;
+  newHeader->metaOffset = 0;
+  newHeader->metaCompLen = 0;
+  newHeader->metaOrigLen = 0;
+  newHeader->privOffset = 0;
+  newHeader->privLen = 0;
+
+  newDir = (woffDirEntry *) ((uint8_t *)newHeader + sizeof(woffHeader));
+
+  tableData = (uint8_t *) malloc(0);
+  if (!tableData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+  tableDataLen = 0;
+
+  for (i = 0; i < numTables; ++i) {
+    uint32_t sourceOffset = READ32BE(origDir[i].offset);
+    uint32_t origLen = READ32BE(origDir[i].origLen);
+    uint32_t compLen = READ32BE(origDir[i].compLen);
+
+    if (sourceOffset + compLen > *woffLen) {
+      FAIL(eWOFF_invalid);
+    }
+
+    newDir[i].tag = origDir[i].tag;
+    newDir[i].checksum = origDir[i].checksum;
+    newDir[i].origLen = origDir[i].origLen;
+
+    if (compLen < origLen) {
+      tableData = (uint8_t *) realloc(tableData, tableDataLen + compLen);
+      if (!tableData) {
+        FAIL(eWOFF_out_of_memory);
+      }
+      memcpy(tableData + tableDataLen, woffData + sourceOffset, compLen);
+      tableOffset = tableDataLen + tableBase;
+      newDir[i].offset = READ32BE(tableOffset);
+      tableDataLen += compLen;
+      tableOffset += compLen;
+    } else {
+      compLen = (origLen + 3) & ~3;
+      tableData = (uint8_t *) realloc(tableData,
+                                      ((tableDataLen + 3) & ~3) + compLen);
+      if (!tableData) {
+        FAIL(eWOFF_out_of_memory);
+      }
+      while ((tableDataLen & 3) != 0) {
+        tableData[tableDataLen++] = 0;
+      }
+      tableOffset = tableDataLen + tableBase;
+      newDir[i].offset = READ32BE(tableOffset);
+      memcpy(tableData + tableDataLen, woffData + sourceOffset, origLen);
+      tableDataLen += origLen;
+      while (origLen++ < compLen) {
+        tableData[tableDataLen++] = 0;
+      }
+    }
+
+    newDir[i].compLen = READ32BE(compLen);
+  }
+
+  totalLength = tableBase + tableDataLen;
+
+  tableOffset = tableDataLen + tableBase;
+  if (metaData && metaCompLen > 0 && metaOrigLen > 0) {
+    newHeader->metaOffset = READ32BE(tableOffset);
+    newHeader->metaCompLen = READ32BE(metaCompLen);
+    newHeader->metaOrigLen = READ32BE(metaOrigLen);
+    tableOffset += metaCompLen;
+    totalLength += metaCompLen;
+  }
+
+  if (privData && privLen > 0) {
+    newHeader->privOffset = READ32BE(tableOffset);
+    newHeader->privLen = READ32BE(privLen);
+    totalLength += privLen;
+  }
+
+  newHeader->length = READ32BE(totalLength);
+
+  newData = (uint8_t *) malloc(totalLength);
+  if (!newData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  memcpy(newData, newHeader, tableBase);
+  free(newHeader);
+  memcpy(newData + tableBase, tableData, tableDataLen);
+  free(tableData);
+
+  tableOffset = tableBase + tableDataLen;
+  if (metaData && metaCompLen > 0 && metaOrigLen > 0) {
+    memcpy(newData + tableOffset, metaData, metaCompLen);
+    tableOffset += metaCompLen;
+  }
+  if (privData && privLen > 0) {
+    memcpy(newData + tableOffset, privData, privLen);
+  }
+
+  *woffLen = totalLength;
+  free((void *) woffData);
+
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return newData;
+
+failure:
+  if (newData) {
+    free(newData);
+  }
+  if (newHeader) {
+    free(newHeader);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+const uint8_t *
+woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen,
+                const uint8_t * metaData, uint32_t metaLen,
+                uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uLong compLen;
+  uint8_t * compData = NULL;
+  const uint8_t * privData = NULL;
+  uint32_t privLen = 0;
+  uint32_t status = eWOFF_ok;
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (header->privOffset != 0 && header->privLen != 0) {
+    privData = woffData + READ32BE(header->privOffset);
+    privLen = READ32BE(header->privLen);
+    if (privData + privLen > woffData + *woffLen) {
+      FAIL(eWOFF_invalid);
+    }
+  }
+
+  compLen = compressBound(metaLen);
+  compData = malloc(compLen);
+  if (!compData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  if (compress2((Bytef *) compData, &compLen,
+                (const Bytef *) metaData, metaLen, 9) != Z_OK) {
+    FAIL(eWOFF_compression_failure);
+  }
+
+  woffData = rebuildWoff(woffData, woffLen,
+                         compData, compLen, metaLen,
+                         privData, privLen, pStatus);
+  free(compData);
+  return woffData;
+
+failure:
+  if (compData) {
+    free(compData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+const uint8_t *
+woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen,
+                   const uint8_t * privData, uint32_t privLen,
+                   uint32_t * pStatus)
+{
+  const woffHeader * header;
+  const uint8_t * metaData = NULL;
+  uint32_t metaLen = 0;
+  uint32_t status = eWOFF_ok;
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (header->metaOffset != 0 && header->metaCompLen != 0) {
+    metaData = woffData + READ32BE(header->metaOffset);
+    metaLen = READ32BE(header->metaCompLen);
+    if (metaData + metaLen > woffData + *woffLen) {
+      FAIL(eWOFF_invalid);
+    }
+  }
+
+  woffData = rebuildWoff(woffData, woffLen,
+                         metaData, metaLen, READ32BE(header->metaOrigLen),
+                         privData, privLen, pStatus);
+  return woffData;
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+/******************************************************************/
+/* * * * * * * * * * * * * * DECODING * * * * * * * * * * * * * * */
+/******************************************************************/
+
+uint32_t
+woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen,
+                   uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint16_t numTables;
+  uint32_t status = eWOFF_ok;
+  uint32_t totalLen = 0;
+
+  if (woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+  numTables = READ32BE(header->numTables);
+  if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) {
+    FAIL(eWOFF_invalid);
+  }
+  totalLen = READ32BE(header->totalOtfSize);
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return totalLen;
+}
+
+void
+woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                   uint8_t * sfntData, uint32_t bufferLen,
+                   uint32_t * pActualSfntLen, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint16_t numTables;
+  const woffDirEntry * woffDir;
+  uint32_t i, totalLen = 0;
+  sfntHeader * newHeader;
+  uint16_t searchRange, rangeShift, entrySelector;
+  uint32_t offset;
+  sfntDirEntry * sfntDir;
+  uint32_t headOffset = 0, headLength = 0;
+  sfntHeadTable * head;
+  uint32_t csum = 0;
+  const uint32_t * csumPtr;
+  uint32_t status = eWOFF_ok;
+
+  /* check basic header fields */
+  if (woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+  if (READ32BE(header->flavor) != SFNT_VERSION_TT &&
+      READ32BE(header->flavor) != SFNT_VERSION_CFF &&
+      READ32BE(header->flavor) != SFNT_VERSION_true) {
+    status |= eWOFF_warn_unknown_version;
+  }
+
+  numTables = READ32BE(header->numTables);
+  if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) {
+    FAIL(eWOFF_invalid);
+  }
+  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+
+  totalLen = READ32BE(header->totalOtfSize);
+  if (bufferLen < totalLen) {
+    FAIL(eWOFF_buffer_too_small);
+  }
+
+  /* construct the sfnt header */
+  newHeader = (sfntHeader *) (sfntData);
+  newHeader->version = header->flavor;
+  newHeader->numTables = READ16BE(numTables);
+  
+  /* calculate header fields for binary search */
+  searchRange = numTables;
+  searchRange |= (searchRange >> 1);
+  searchRange |= (searchRange >> 2);
+  searchRange |= (searchRange >> 4);
+  searchRange |= (searchRange >> 8);
+  searchRange &= ~(searchRange >> 1);
+  searchRange *= 16;
+  newHeader->searchRange = READ16BE(searchRange);
+  rangeShift = numTables * 16 - searchRange;
+  newHeader->rangeShift = READ16BE(rangeShift);
+  entrySelector = 0;
+  while (searchRange > 16) {
+    ++entrySelector;
+    searchRange >>= 1;
+  }
+  newHeader->entrySelector = READ16BE(entrySelector);
+
+  /* process each table, filling in the sfnt directory */
+  offset = 12 + numTables * sizeof(sfntDirEntry);
+  sfntDir = (sfntDirEntry *) (sfntData + sizeof(sfntHeader));
+  for (i = 0; i < numTables; ++i) {
+    uint32_t origLen, compLen;
+
+    origLen = READ32BE(woffDir[i].origLen);
+    compLen = READ32BE(woffDir[i].compLen);
+    if (origLen > totalLen || offset > totalLen - origLen) {
+      FAIL(eWOFF_buffer_too_small);
+    }
+
+    sfntDir[i].tag = woffDir[i].tag;
+    sfntDir[i].offset = READ32BE(offset);
+    sfntDir[i].length = woffDir[i].origLen;
+    sfntDir[i].checksum = woffDir[i].checksum;
+    csum += READ32BE(sfntDir[i].checksum);
+
+    if (compLen < origLen) {
+      uLongf destLen = origLen;
+      if (uncompress((Bytef *)(sfntData + offset), &destLen,
+                     (const Bytef *)(woffData + READ32BE(woffDir[i].offset)),
+                     compLen) != Z_OK || destLen != origLen) {
+        break;
+      }
+    } else {
+      memcpy(sfntData + offset,
+             woffData + READ32BE(woffDir[i].offset), origLen);
+    }
+
+    if (READ32BE(sfntDir[i].tag) == TABLE_TAG_head) {
+      headOffset = offset;
+      headLength = origLen;
+    }
+
+    offset += origLen;
+    while (offset < totalLen && (offset & 3) != 0) {
+      sfntData[offset++] = 0;
+    }
+  }
+  if (i < numTables) {
+    FAIL(eWOFF_compression_failure);
+  }
+
+  /* the font checksum in the 'head' table depends on all the individual
+     table checksums (collected above), plus the header and directory
+     which are added in here */
+  if (headOffset == 0 || headLength < HEAD_TABLE_SIZE) {
+    FAIL(eWOFF_invalid);
+  }
+  head = (sfntHeadTable *)(sfntData + headOffset);
+  head->checkSumAdjustment = 0;
+  csumPtr = (const uint32_t *)sfntData;
+  while (csumPtr < (const uint32_t *)(sfntData + 12 +
+                                      numTables * sizeof(sfntDirEntry))) {
+    csum += READ32BE(*csumPtr);
+    csumPtr++;
+  }
+  csum = 0xb1b0afbaU - csum;
+  head->checkSumAdjustment = READ32BE(csum);
+
+  if (pActualSfntLen) {
+    *pActualSfntLen = totalLen;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return;
+
+failure:
+  if (pActualSfntLen) {
+    *pActualSfntLen = 0;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
+
+const uint8_t *
+woffDecode(const uint8_t * woffData, uint32_t woffLen,
+           uint32_t * sfntLen, uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  uint8_t * sfntData = NULL;
+  uint32_t bufLen = woffGetDecodedSize(woffData, woffLen, &status);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  sfntData = (uint8_t *) malloc(bufLen);
+  if (!sfntData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  woffDecodeToBuffer(woffData, woffLen, sfntData, bufLen, sfntLen, &status);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return sfntData;
+
+failure:
+  if (sfntData) {
+    free(sfntData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+const uint8_t *
+woffGetMetadata(const uint8_t * woffData, uint32_t woffLen,
+                uint32_t * metaLen, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t offset, compLen;
+  uLong origLen;
+  uint8_t * data = NULL;
+  uint32_t status = eWOFF_ok;
+
+  if (woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (READ32BE(header->length) != woffLen) {
+    FAIL(eWOFF_invalid);
+  }
+
+  offset = READ32BE(header->metaOffset);
+  compLen = READ32BE(header->metaCompLen);
+  origLen = READ32BE(header->metaOrigLen);
+  if (offset == 0 || compLen == 0 || origLen == 0) {
+    if (pStatus) {
+      *pStatus |= eWOFF_warn_block_missing;
+    }
+    return NULL;
+  }
+
+  if (offset + compLen > woffLen) {
+    FAIL(eWOFF_invalid);
+  }
+
+  data = malloc(origLen);
+  if (!data) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  if (uncompress((Bytef *)data, &origLen,
+                 (const Bytef *)woffData + offset, compLen) != Z_OK ||
+      origLen != READ32BE(header->metaOrigLen)) {
+    FAIL(eWOFF_compression_failure);
+  }
+
+  if (metaLen) {
+    *metaLen = origLen;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return data;
+
+failure:
+  if (data) {
+    free(data);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;    
+}
+
+const uint8_t *
+woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen,
+                   uint32_t * privLen, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t offset, length;
+  uint8_t * data = NULL;
+  uint32_t status = eWOFF_ok;
+
+  if (woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (READ32BE(header->length) != woffLen) {
+    FAIL(eWOFF_invalid);
+  }
+
+  offset = READ32BE(header->privOffset);
+  length = READ32BE(header->privLen);
+  if (offset == 0 || length == 0) {
+    if (pStatus) {
+      *pStatus |= eWOFF_warn_block_missing;
+    }
+    return NULL;
+  }
+
+  if (offset + length > woffLen) {
+    FAIL(eWOFF_invalid);
+  }
+
+  data = malloc(length);
+  if (!data) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  memcpy(data, woffData + offset, length);
+
+  if (privLen) {
+    *privLen = length;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return data;
+
+failure:
+  if (data) {
+    free(data);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;    
+}
+
+void
+woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen,
+                   uint16_t * major, uint16_t * minor, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t offset, length;
+  uint8_t * data = NULL;
+  uint32_t status = eWOFF_ok;
+
+  *major = *minor = 0;
+
+  if (woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  *major = READ16BE(header->majorVersion);
+  *minor = READ16BE(header->minorVersion);
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
diff --git a/gfx/thebes/src/woff.h b/gfx/thebes/src/woff.h
new file mode 100755
--- /dev/null
+++ b/gfx/thebes/src/woff.h
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef WOFF_H_
+#define WOFF_H_
+
+/* API for the WOFF encoder and decoder */
+
+#ifdef _MSC_VER /* MS VC lacks inttypes.h
+                   but we can make do with a few definitons here */
+typedef char           int8_t;
+typedef short          int16_t;
+typedef int            int32_t;
+typedef unsigned char  uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+#else
+#include <inttypes.h>
+#endif
+
+/* error codes returned in the status parameter of WOFF functions */
+enum {
+  /* Success */
+  eWOFF_ok = 0,
+
+  /* Errors: no valid result returned */
+  eWOFF_out_of_memory = 1,       /* malloc or realloc failed */
+  eWOFF_invalid = 2,             /* invalid input file (e.g., bad offset) */
+  eWOFF_compression_failure = 3, /* error in zlib call */
+  eWOFF_bad_signature = 4,       /* unrecognized file signature */
+  eWOFF_buffer_too_small = 5,    /* the provided buffer is too small */
+
+  /* Warnings: call succeeded but something odd was noticed.
+     Multiple warnings may be OR'd together. */
+  eWOFF_warn_unknown_version = 0x0100, /* unrecognized version of sfnt,
+                                          not standard TrueType or CFF */
+  eWOFF_warn_block_missing = 0x0200    /* requested block (metadata/private)
+                                          not present in the WOFF font */
+};
+
+#define WOFF_SUCCESS(status) (((uint32_t)(status) & 0xff) == eWOFF_ok)
+#define WOFF_FAILURE(status) (!WOFF_SUCCESS(status))
+#define WOFF_WARNING(status) ((uint32_t)(status) & ~0xff)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WOFF_DISABLE_ENCODING
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the encoded data, or NULL on error;
+ * caller should free() this when finished with it.
+ * Returns length of the encoded data in woffLen.
+ * The new WOFF has no metadata or private block;
+ * see the following functions to update these elements.
+ */
+const uint8_t * woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
+                           uint16_t majorVersion, uint16_t minorVersion,
+                           uint32_t * woffLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Add the given metadata block to the WOFF font, replacing any existing
+ * metadata block. The block will be zlib-compressed.
+ * Metadata is required to be valid XML (use of UTF-8 is recommended),
+ * though this function does not currently check this.
+ * The woffData pointer must be a malloc() block (typically from woffEncode);
+ * it will be freed by this function and a new malloc() block will be returned.
+ * Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
+ */
+const uint8_t * woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen,
+                                const uint8_t * metaData, uint32_t metaLen,
+                                uint32_t * status);
+
+
+/*****************************************************************************
+ * Add the given private data block to the WOFF font, replacing any existing
+ * private block. The block will NOT be zlib-compressed.
+ * Private data may be any arbitrary block of bytes; it may be externally
+ * compressed by the client if desired.
+ * The woffData pointer must be a malloc() block (typically from woffEncode);
+ * it will be freed by this function and a new malloc() block will be returned.
+ * Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
+ */
+const uint8_t * woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen,
+                                   const uint8_t * privData, uint32_t privLen,
+                                   uint32_t * status);
+
+#endif /* WOFF_DISABLE_ENCODING */
+
+/*****************************************************************************
+ * Returns the size of buffer needed to decode the font (or zero on error).
+ */
+uint32_t woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen,
+                            uint32_t * pStatus);
+
+
+/*****************************************************************************
+ * Decodes WOFF font to a caller-supplied buffer of size bufferLen.
+ * Returns the actual size of the decoded sfnt data in pActualSfntLen
+ * (must be <= bufferLen, otherwise an error will be returned).
+ */
+void woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                        uint8_t * sfntData, uint32_t bufferLen,
+                        uint32_t * pActualSfntLen, uint32_t * pStatus);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the decoded data, or NULL on error;
+ * caller should free() this when finished with it.
+ * Returns length of the decoded data in sfntLen.
+ */
+const uint8_t * woffDecode(const uint8_t * woffData, uint32_t woffLen,
+                           uint32_t * sfntLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the metadata from the WOFF font,
+ * or NULL if an error occurs or no metadata is present.
+ * Length of the metadata is returned in metaLen.
+ * The metadata is decompressed before returning.
+ */
+const uint8_t * woffGetMetadata(const uint8_t * woffData, uint32_t woffLen,
+                                uint32_t * metaLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the private data from the WOFF font,
+ * or NULL if an error occurs or no private data is present.
+ * Length of the private data is returned in privLen.
+ */
+const uint8_t * woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen,
+                                   uint32_t * privLen, uint32_t * status);
+
+/*****************************************************************************
+ * Returns the font version numbers from the WOFF font in the major and minor
+ * parameters.
+ * Check the status result to know if the function succeeded.
+ */
+void woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen,
+                        uint16_t * major, uint16_t * minor,
+                        uint32_t * status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -126,19 +126,17 @@ nsFontFaceLoader::OnStreamComplete(nsISt
                "We should have been canceled already");
 
   // whether an error occurred or not, notify the user font set of the completion
   gfxUserFontSet *userFontSet = ctx->GetUserFontSet();
   if (!userFontSet) {
     return aStatus;
   }
   
-  PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry, aLoader,
-                                                  aString, aStringLen,
-                                                  aStatus);
+  PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry, aLoader, aStatus);
 
   // when new font loaded, need to reflow
   if (fontUpdate) {
     // Update layout for the presence of the new font.  Since this is
     // asynchronous, reflows will coalesce.
     ctx->UserFontSetUpdated();
     LOG(("fontdownloader (%p) reflow\n", this));
   }
diff --git a/netwerk/base/public/nsIStreamLoader.idl b/netwerk/base/public/nsIStreamLoader.idl
--- a/netwerk/base/public/nsIStreamLoader.idl
+++ b/netwerk/base/public/nsIStreamLoader.idl
@@ -32,19 +32,26 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIStreamListener.idl"
 
+%{C++
+#include "nsTArray.h"
+#include "prtypes.h"
+%}
+
 interface nsIRequest;
 interface nsIStreamLoader;
 
+[ptr] native PRUint8Array(nsTArray<PRUint8>);
+
 [scriptable, uuid(359F7990-D4E9-11d3-A1A5-0050041CAF44)]
 interface nsIStreamLoaderObserver : nsISupports
 {
     /**
      * Called when the entire stream has been loaded.
      *
      * @param loader the stream loader that loaded the stream.
      * @param ctxt the context parameter of the underlying channel
@@ -69,28 +76,37 @@ interface nsIStreamLoaderObserver : nsIS
  * To use this interface, first call init() with a nsIStreamLoaderObserver
  * that will be notified when the data has been loaded. Then call asyncOpen()
  * on the channel with the nsIStreamLoader as the listener. The context
  * argument in the asyncOpen() call will be passed to the onStreamComplete()
  * callback.
  *
  * XXX define behaviour for sizes >4 GB
  */
-[scriptable, uuid(8ea7e890-8211-11d9-8bde-f66bad1e3f3a)]
+[scriptable, uuid(E260C96B-A1F0-4C0E-B73B-BFD56F1E3B29)]
 interface nsIStreamLoader : nsIStreamListener
 {
     /**
      * Initialize this stream loader, and start loading the data.
      *
      * @param aObserver
      *        An observer that will be notified when the data is complete.
      */
     void init(in nsIStreamLoaderObserver aObserver);
 
     /**
+     * Extract the loader's data into a caller-supplied nsTArray;
+     * after this call, the loader will no longer contain the data.
+     *
+     * @param aDestination
+     *        An array that will receive the data from the loader.
+     */
+    [noscript] void extractData(in PRUint8Array aDestination);
+
+    /**
      * Gets the number of bytes read so far.
      */
     readonly attribute unsigned long numBytesRead;
 
     /**
      * Gets the request that loaded this file.
      * null after the request has finished loading.
      */
diff --git a/netwerk/base/src/nsStreamLoader.cpp b/netwerk/base/src/nsStreamLoader.cpp
--- a/netwerk/base/src/nsStreamLoader.cpp
+++ b/netwerk/base/src/nsStreamLoader.cpp
@@ -99,18 +99,17 @@ nsStreamLoader::OnStopRequest(nsIRequest
 nsStreamLoader::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
                               nsresult aStatus)
 {
   if (mObserver) {
     // provide nsIStreamLoader::request during call to OnStreamComplete
     mRequest = request;
     mObserver->OnStreamComplete(this, mContext, aStatus, 
                                 mData.Length(),
-                                reinterpret_cast<const PRUint8*>
-                                                (mData.get()));
+                                mData.Elements());
     // done.. cleanup
     mRequest = 0;
     mObserver = 0;
     mContext = 0;
   }
   return NS_OK;
 }
 
@@ -119,22 +118,32 @@ nsStreamLoader::WriteSegmentFun(nsIInput
                                 void *closure,
                                 const char *fromSegment,
                                 PRUint32 toOffset,
                                 PRUint32 count,
                                 PRUint32 *writeCount)
 {
   nsStreamLoader *self = (nsStreamLoader *) closure;
 
-  self->mData.Append(fromSegment, count);
+  self->mData.AppendElements((PRUint8 *) fromSegment, count);
   *writeCount = count;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, 
                                 nsIInputStream *inStr, 
                                 PRUint32 sourceOffset, PRUint32 count)
 {
   PRUint32 countRead;
   return inStr->ReadSegments(WriteSegmentFun, this, count, &countRead);
 }
+
+NS_IMETHODIMP
+nsStreamLoader::ExtractData(nsTArray<PRUint8>* aDestination)
+{
+  aDestination->Clear();
+  if (aDestination->SwapElements(mData)) {
+    return NS_OK;
+  }
+  return NS_ERROR_UNEXPECTED;
+}
diff --git a/netwerk/base/src/nsStreamLoader.h b/netwerk/base/src/nsStreamLoader.h
--- a/netwerk/base/src/nsStreamLoader.h
+++ b/netwerk/base/src/nsStreamLoader.h
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsStreamLoader_h__
 #define nsStreamLoader_h__
 
 #include "nsIRequest.h"
 #include "nsIStreamLoader.h"
 #include "nsCOMPtr.h"
-#include "nsString.h"
+#include "nsTArray.h"
 
 class nsStreamLoader : public nsIStreamLoader
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLOADER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
@@ -58,13 +58,13 @@ public:
   Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
 
 protected:
   static NS_METHOD WriteSegmentFun(nsIInputStream *, void *, const char *,
                                    PRUint32, PRUint32, PRUint32 *);
 
   nsCOMPtr<nsIStreamLoaderObserver> mObserver;
   nsCOMPtr<nsISupports>             mContext;  // the observer's context
-  nsCString                         mData;
+  nsTArray<PRUint8>                 mData;
   nsCOMPtr<nsIRequest>              mRequest;
 };
 
 #endif // nsStreamLoader_h__
