From 37bbb8bf3d4806cb3f102505ddcddd1ed7e829a8 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Sun, 30 Aug 2009 14:55:24 +0100 Subject: [PATCH] Bug 507970, part 2: implement support for downloadable WOFF fonts, r=jdaggett --HG-- extra : rebase_source : e68c49d2dbea815e453f9ab52ba89bd9127d3f51 --- gfx/thebes/public/gfxFT2Fonts.h | 10 +- gfx/thebes/public/gfxFontUtils.h | 20 +- gfx/thebes/public/gfxPangoFonts.h | 1 - gfx/thebes/public/gfxPlatform.h | 8 +- gfx/thebes/public/gfxPlatformGtk.h | 1 - gfx/thebes/public/gfxPlatformMac.h | 1 - gfx/thebes/public/gfxUserFontSet.h | 4 +- gfx/thebes/public/gfxWindowsFonts.h | 1 - gfx/thebes/public/gfxWindowsPlatform.h | 1 - gfx/thebes/src/Makefile.in | 4 +- gfx/thebes/src/gfxFT2Fonts.cpp | 55 +- gfx/thebes/src/gfxFontUtils.cpp | 47 +- gfx/thebes/src/gfxMacPlatformFontList.mm | 24 +- gfx/thebes/src/gfxPangoFonts.cpp | 25 +- gfx/thebes/src/gfxPlatform.cpp | 15 + gfx/thebes/src/gfxPlatformFontList.h | 1 + gfx/thebes/src/gfxPlatformGtk.cpp | 9 +- gfx/thebes/src/gfxPlatformMac.cpp | 10 +- gfx/thebes/src/gfxUserFontSet.cpp | 110 +- gfx/thebes/src/gfxWindowsFonts.cpp | 9 +- gfx/thebes/src/gfxWindowsPlatform.cpp | 11 +- gfx/thebes/src/woff-private.h | 159 +++ gfx/thebes/src/woff.c | 1170 ++++++++++++++++++++++ gfx/thebes/src/woff.h | 211 ++++ layout/style/nsFontFaceLoader.cpp | 9 +- 25 files changed, 1816 insertions(+), 100 deletions(-) create mode 100644 gfx/thebes/src/woff-private.h create mode 100644 gfx/thebes/src/woff.c create mode 100644 gfx/thebes/src/woff.h diff --git a/gfx/thebes/public/gfxFT2Fonts.h b/gfx/thebes/public/gfxFT2Fonts.h index afb99fae7a0a..5f26b3801a83 100644 --- a/gfx/thebes/public/gfxFT2Fonts.h +++ b/gfx/thebes/public/gfxFT2Fonts.h @@ -86,12 +86,14 @@ public: } static FontEntry* - CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, nsISupports *aLoader, + CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength); - + static FontEntry* - CreateFontEntryFromFace(FT_Face aFace); - + CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData = nsnull); + // aFontData is NS_Malloc'ed data that aFace depends on, to be freed + // after the face is destroyed; null if there is no such buffer + cairo_font_face_t *CairoFontFace(); nsresult ReadCMAP(); diff --git a/gfx/thebes/public/gfxFontUtils.h b/gfx/thebes/public/gfxFontUtils.h index de3bd8905254..397a0ff3e3b9 100644 --- a/gfx/thebes/public/gfxFontUtils.h +++ b/gfx/thebes/public/gfxFontUtils.h @@ -359,6 +359,13 @@ struct FontDataOverlay { 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: @@ -484,13 +491,22 @@ public: static nsresult MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength, nsTArray *aHeader, FontDataOverlay *aOverlay); + + // determine whether a font (which has already passed ValidateSFNTHeaders) + // is CFF format rather than TrueType + static PRBool + IsCffFont(const PRUint8* aFontData); + #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); + ValidateSFNTHeaders(const PRUint8 *aFontData, PRUint32 aFontDataLength); // 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 index ebec2d366bc2..caf79f814e38 100644 --- a/gfx/thebes/public/gfxPangoFonts.h +++ b/gfx/thebes/public/gfxPangoFonts.h @@ -88,7 +88,6 @@ public: const nsAString &aFullname); // Used for @font-face { src: url(); } static gfxFontEntry *NewFontEntry(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength); diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h index 084d9ce9bf40..e9cff1e6d1b6 100644 --- a/gfx/thebes/public/gfxPlatform.h +++ b/gfx/thebes/public/gfxPlatform.h @@ -211,15 +211,15 @@ public: /** * Activate a platform font. (Needed to support @font-face src url().) - * aFontData must persist as long as a reference is held to aLoader. + * aFontData is a NS_Malloc'ed block that must be freed by this function + * (or responsibility passed on) when it is no longer needed; the caller + * will NOT free it. * 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) - { return nsnull; } + PRUint32 aLength); /** * Whether to allow downloadable fonts via @font-face rules diff --git a/gfx/thebes/public/gfxPlatformGtk.h b/gfx/thebes/public/gfxPlatformGtk.h index 3053922c919b..9c898716ca9d 100644 --- a/gfx/thebes/public/gfxPlatformGtk.h +++ b/gfx/thebes/public/gfxPlatformGtk.h @@ -102,7 +102,6 @@ public: * */ virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength); diff --git a/gfx/thebes/public/gfxPlatformMac.h b/gfx/thebes/public/gfxPlatformMac.h index 3920b7230a75..e13366b44183 100644 --- a/gfx/thebes/public/gfxPlatformMac.h +++ b/gfx/thebes/public/gfxPlatformMac.h @@ -78,7 +78,6 @@ public: virtual gfxPlatformFontList* CreatePlatformFontList(); virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength); diff --git a/gfx/thebes/public/gfxUserFontSet.h b/gfx/thebes/public/gfxUserFontSet.h index b2a630f531ce..d2239729974a 100644 --- a/gfx/thebes/public/gfxUserFontSet.h +++ b/gfx/thebes/public/gfxUserFontSet.h @@ -197,7 +197,9 @@ public: // 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, + // Ownership of aFontData is passed in here; the font set must + // ensure that it is eventually deleted with NS_Free(). + PRBool OnLoadComplete(gfxFontEntry *aFontToLoad, const PRUint8 *aFontData, PRUint32 aLength, nsresult aDownloadStatus); diff --git a/gfx/thebes/public/gfxWindowsFonts.h b/gfx/thebes/public/gfxWindowsFonts.h index b174bbe3c7d9..6953da5f5648 100644 --- a/gfx/thebes/public/gfxWindowsFonts.h +++ b/gfx/thebes/public/gfxWindowsFonts.h @@ -140,7 +140,6 @@ public: // create a font entry from downloaded font data static FontEntry* LoadFont(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength); diff --git a/gfx/thebes/public/gfxWindowsPlatform.h b/gfx/thebes/public/gfxWindowsPlatform.h index f51ef9e14381..f50dc90c3f50 100644 --- a/gfx/thebes/public/gfxWindowsPlatform.h +++ b/gfx/thebes/public/gfxWindowsPlatform.h @@ -126,7 +126,6 @@ public: * Activate a platform font (needed to support @font-face src url() ) */ virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength); diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in index cc4be5b053b7..5a7bf2133d08 100644 --- a/gfx/thebes/src/Makefile.in +++ b/gfx/thebes/src/Makefile.in @@ -154,9 +154,11 @@ CMMSRCS = gfxMacPlatformFontList.mm EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL -framework QuickTime endif +CSRCS += woff.c + EXTRA_DSO_LDOPTS += $(TK_LIBS) -DEFINES += -DIMPL_THEBES +DEFINES += -DIMPL_THEBES -DWOFF_MOZILLA_CLIENT include $(topsrcdir)/config/rules.mk diff --git a/gfx/thebes/src/gfxFT2Fonts.cpp b/gfx/thebes/src/gfxFT2Fonts.cpp index 95d9b1a8ee8c..5821a24a5621 100644 --- a/gfx/thebes/src/gfxFT2Fonts.cpp +++ b/gfx/thebes/src/gfxFT2Fonts.cpp @@ -117,33 +117,56 @@ FontEntry::~FontEntry() /* static */ FontEntry* FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, - PRUint32 aLength) { - if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) - return nsnull; + const PRUint8 *aFontData, + PRUint32 aLength) +{ + // Ownership of aFontData is passed in here; the fontEntry must + // retain it as long as the FT_Face needs it, and ensure it is + // eventually deleted. FT_Face face; FT_Error error = FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), aFontData, aLength, 0, &face); - if (error != FT_Err_Ok) + if (error != FT_Err_Ok) { + NS_Free((void*)aFontData); return nsnull; - FontEntry* fe = FontEntry::CreateFontEntryFromFace(face); + } + FontEntry* fe = FontEntry::CreateFontEntryFromFace(face, aFontData); fe->mItalic = aProxyEntry.mItalic; fe->mWeight = aProxyEntry.mWeight; fe->mStretch = aProxyEntry.mStretch; return fe; - } +class FTUserFontData { +public: + FTUserFontData(FT_Face aFace, const PRUint8* aData) + : mFace(aFace), mFontData(aData) + { + } + + ~FTUserFontData() + { + FT_Done_Face(mFace); + if (mFontData) { + NS_Free((void*)mFontData); + } + } + +private: + FT_Face mFace; + const PRUint8 *mFontData; +}; + static void FTFontDestroyFunc(void *data) { - FT_Face face = (FT_Face)data; - FT_Done_Face(face); + FTUserFontData *userFontData = static_cast(data); + delete userFontData; } /* static */ FontEntry* -FontEntry::CreateFontEntryFromFace(FT_Face aFace) { +FontEntry::CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData) { static cairo_user_data_key_t key; if (!aFace->family_name) { @@ -161,10 +184,12 @@ FontEntry::CreateFontEntryFromFace(FT_Face aFace) { fe->mItalic = aFace->style_flags & FT_STYLE_FLAG_ITALIC; fe->mFTFace = aFace; fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, 0); - cairo_font_face_set_user_data(fe->mFontFace, &key, - aFace, FTFontDestroyFunc); - TT_OS2 *os2 = static_cast(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2)); + FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData); + cairo_font_face_set_user_data(fe->mFontFace, &key, + userFontData, FTFontDestroyFunc); + + TT_OS2 *os2 = static_cast(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2)); PRUint16 os2weight = 0; if (os2 && os2->version != 0xffff) { // Technically, only 100 to 900 are valid, but some fonts @@ -205,7 +230,9 @@ FontEntry::CairoFontFace() FT_New_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), mFilename.get(), mFTFontIndex, &face); mFTFace = face; mFontFace = cairo_ft_font_face_create_for_ft_face(face, 0); - cairo_font_face_set_user_data(mFontFace, &key, face, FTFontDestroyFunc); + FTUserFontData *userFontData = new FTUserFontData(face, nsnull); + cairo_font_face_set_user_data(mFontFace, &key, + userFontData, FTFontDestroyFunc); } return mFontFace; } diff --git a/gfx/thebes/src/gfxFontUtils.cpp b/gfx/thebes/src/gfxFontUtils.cpp index db9171075777..98232d1ddae0 100644 --- a/gfx/thebes/src/gfxFontUtils.cpp +++ b/gfx/thebes/src/gfxFontUtils.cpp @@ -52,6 +52,8 @@ #include "plbase64.h" +#include "woff.h" + #ifdef XP_MACOSX #include #endif @@ -694,12 +696,38 @@ ValidateKernTable(const PRUint8 *aKernTable, PRUint32 aKernLength) 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(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(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) + PRUint32 aFontDataLength) { - NS_ASSERTION(aFontData && aFontDataLength != 0, "null font data"); + NS_ASSERTION(aFontData, "null font data"); PRUint64 dataLength(aFontDataLength); @@ -716,9 +744,6 @@ gfxFontUtils::ValidateSFNTHeaders(const PRUint8 *aFontData, return PR_FALSE; } - if (aIsCFF) - *aIsCFF = (sfntVersion == TRUETYPE_TAG('O','T','T','O')); - // iterate through the table headers to find the head, name and OS/2 tables PRBool foundHead = PR_FALSE, foundOS2 = PR_FALSE, foundName = PR_FALSE; PRBool foundGlyphs = PR_FALSE, foundCFF = PR_FALSE, foundKern = PR_FALSE; @@ -1733,4 +1758,14 @@ gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength, return NS_OK; } +/* static */ +PRBool +gfxFontUtils::IsCffFont(const PRUint8* aFontData) +{ + // this is only called after aFontData has passed basic validation, + // so we know there is enough data present to allow us to read the version! + const SFNTHeader *sfntHeader = reinterpret_cast(aFontData); + return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O')); +} + #endif diff --git a/gfx/thebes/src/gfxMacPlatformFontList.mm b/gfx/thebes/src/gfxMacPlatformFontList.mm index ccedcb038e10..0bca5b01025d 100644 --- a/gfx/thebes/src/gfxMacPlatformFontList.mm +++ b/gfx/thebes/src/gfxMacPlatformFontList.mm @@ -690,27 +690,13 @@ public: }; gfxFontEntry* -gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry, - const PRUint8 *aFontData, PRUint32 aLength) +gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry, + const PRUint8 *aFontData, + PRUint32 aLength) { OSStatus err; - NS_ASSERTION(aFontData && aLength != 0, - "MakePlatformFont called with null data ptr"); - - // do simple validation check on font data before - // attempting to activate it - if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) { -#if DEBUG - char warnBuf[1024]; - const gfxProxyFontEntry *proxyEntry = - static_cast (aProxyEntry); - sprintf(warnBuf, "downloaded font error, invalid font data for (%s)", - NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get()); - NS_WARNING(warnBuf); -#endif - return nsnull; - } + NS_ASSERTION(aFontData, "MakePlatformFont called with null data"); ATSFontRef fontRef; ATSFontContainerRef containerRef; @@ -720,7 +706,7 @@ gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry, const PRUint32 kMaxRetries = 3; PRUint32 retryCount = 0; while (retryCount++ < kMaxRetries) { - err = ::ATSFontActivateFromMemory(const_cast(aFontData), aLength, + err = ::ATSFontActivateFromMemory(const_cast(aFontData), aLength, kPrivateATSFontContextPrivate, kATSFontFormatUnspecified, NULL, diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp index 316a64a399fb..7542226e6716 100644 --- a/gfx/thebes/src/gfxPangoFonts.cpp +++ b/gfx/thebes/src/gfxPangoFonts.cpp @@ -300,10 +300,10 @@ public: class gfxDownloadedFcFontEntry : public gfxFcFontEntry { public: - // This takes ownership of the face. + // This takes ownership of the face and its underlying data gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader, FT_Face aFace) - : gfxFcFontEntry(aProxyEntry), mLoader(aLoader), mFace(aFace) + const PRUint8 *aData, FT_Face aFace) + : gfxFcFontEntry(aProxyEntry), mFontData(aData), mFace(aFace) { NS_PRECONDITION(aFace != NULL, "aFace is NULL!"); InitPattern(); @@ -319,9 +319,13 @@ public: protected: virtual void InitPattern(); - // mLoader holds a reference to memory used by mFace. - nsCOMPtr mLoader; + // mFontData holds the data used to instantiate the FT_Face; + // this has to persist until we are finished with the face, + // then be released with NS_Free(). + const PRUint8* mFontData; + 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). @@ -371,6 +375,7 @@ gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry() FcPatternDel(mPatterns[0], FC_FT_FACE); } FT_Done_Face(mFace); + NS_Free((void*)mFontData); } typedef FcPattern* (*QueryFaceFunction)(const FT_Face face, @@ -2218,18 +2223,22 @@ GetFTLibrary() /* static */ gfxFontEntry * gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength) { + // Ownership of aFontData is passed in here, and transferred to the + // new fontEntry, which will release it when no longer needed. + // 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); - if (error != 0) + if (error != 0) { + NS_Free((void*)aFontData); return nsnull; + } - return new gfxDownloadedFcFontEntry(aProxyEntry, aLoader, face); + return new gfxDownloadedFcFontEntry(aProxyEntry, aFontData, face); } diff --git a/gfx/thebes/src/gfxPlatform.cpp b/gfx/thebes/src/gfxPlatform.cpp index d96fe9df6c1a..eb50463b4881 100644 --- a/gfx/thebes/src/gfxPlatform.cpp +++ b/gfx/thebes/src/gfxPlatform.cpp @@ -327,6 +327,21 @@ gfxPlatform::DownloadableFontsEnabled() return allowDownloadableFonts; } +gfxFontEntry* +gfxPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, + const PRUint8 *aFontData, + PRUint32 aLength) +{ + // Default implementation does not handle activating downloaded fonts; + // just free the data and return. + // Platforms that support @font-face must override this, + // using the data to instantiate the font, and taking responsibility + // for freeing it when no longer required. + if (aFontData) { + NS_Free((void*)aFontData); + } + return nsnull; +} static void AppendGenericFontFromPref(nsString& aFonts, const char *aLangGroup, const char *aGenericName) diff --git a/gfx/thebes/src/gfxPlatformFontList.h b/gfx/thebes/src/gfxPlatformFontList.h index 1f440e94db56..f81c9fcd2a8a 100644 --- a/gfx/thebes/src/gfxPlatformFontList.h +++ b/gfx/thebes/src/gfxPlatformFontList.h @@ -108,6 +108,7 @@ public: const nsAString& aFontName) = 0; // create a new platform font from downloaded data (@font-face) + // this method is responsible to ensure aFontData is NS_Free()'d virtual gfxFontEntry* MakePlatformFont(const gfxFontEntry *aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength) = 0; diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp index 5a92c4534397..3d168d3475e6 100644 --- a/gfx/thebes/src/gfxPlatformGtk.cpp +++ b/gfx/thebes/src/gfxPlatformGtk.cpp @@ -308,15 +308,10 @@ gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, gfxFontEntry* gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength) { - // Just being consistent with other platforms. - // This will mean that only fonts in SFNT formats will be accepted. - if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) - return nsnull; - - return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aLoader, + // passing ownership of the font data to the new font entry + return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontData, aLength); } diff --git a/gfx/thebes/src/gfxPlatformMac.cpp b/gfx/thebes/src/gfxPlatformMac.cpp index d3064db1b784..b3bfb523a7a1 100644 --- a/gfx/thebes/src/gfxPlatformMac.cpp +++ b/gfx/thebes/src/gfxPlatformMac.cpp @@ -187,10 +187,16 @@ gfxPlatformMac::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, gfxFontEntry* gfxPlatformMac::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength) { - return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, aFontData, aLength); + // Ownership of aFontData is passed in here. + // After activating the font via ATS, we can discard the data. + gfxFontEntry *fe = + gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, + aFontData, + aLength); + NS_Free((void*)aFontData); + return fe; } PRBool diff --git a/gfx/thebes/src/gfxUserFontSet.cpp b/gfx/thebes/src/gfxUserFontSet.cpp index 6ff4135fce03..ab1bd5251ac2 100644 --- a/gfx/thebes/src/gfxUserFontSet.cpp +++ b/gfx/thebes/src/gfxUserFontSet.cpp @@ -15,11 +15,12 @@ * 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 + * Jonathan Kew * * 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 @@ -46,6 +47,8 @@ #include "nsUnicharUtils.h" #include "prlong.h" +#include "woff.h" + #ifdef PR_LOGGING static PRLogModuleInfo *gUserFontsLog = PR_NewLogModule("userfonts"); #endif /* PR_LOGGING */ @@ -87,7 +90,6 @@ gfxUserFontSet::gfxUserFontSet() gfxUserFontSet::~gfxUserFontSet() { - } void @@ -169,25 +171,99 @@ gfxUserFontSet::FindFontEntry(const nsAString& aName, return nsnull; } +// Given a buffer of downloaded font data, do any necessary preparation +// to make it into usable OpenType. +// May return the original pointer unchanged, or a newly-allocated +// block (in which case the passed-in block is NS_Free'd). +// aLength is updated if necessary to the new length of the data. +// Returns NULL and NS_Free's the incoming data in case of errors. +const PRUint8* +PrepareOpenTypeData(const PRUint8* aData, PRUint32* aLength) +{ + switch(gfxFontUtils::DetermineFontDataType(aData, *aLength)) { + + case GFX_USERFONT_OPENTYPE: + // nothing to do + return aData; + + case GFX_USERFONT_WOFF: { + PRUint32 status = eWOFF_ok; + PRUint32 bufferSize = woffGetDecodedSize(aData, *aLength, &status); + if (WOFF_FAILURE(status)) { + break; + } + PRUint8* decodedData = static_cast(NS_Alloc(bufferSize)); + if (!decodedData) { + break; + } + woffDecodeToBuffer(aData, *aLength, + decodedData, bufferSize, + aLength, &status); + // replace original data with the decoded version + NS_Free((void*)aData); + aData = decodedData; + if (WOFF_FAILURE(status)) { + // something went wrong, discard the data and return NULL + break; + } + // success, return the decoded data + return aData; + } + // xxx - add support for other wrappers here + + default: + NS_WARNING("unknown font format"); + break; + } + + // discard downloaded data that couldn't be used + NS_Free((void*)aData); + + return nsnull; +} + +// This is called when a font download finishes. +// Ownership of aFontData passes in here, and the font set must +// ensure that it is eventually deleted via NS_Free(). PRBool gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength, nsresult aDownloadStatus) { NS_ASSERTION(aFontToLoad->mIsProxy, "trying to load font data for wrong font entry type"); - if (!aFontToLoad->mIsProxy) + if (!aFontToLoad->mIsProxy) { + NS_Free((void*)aFontData); return PR_FALSE; + } gfxProxyFontEntry *pe = static_cast (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; + + // Unwrap/decompress or otherwise munge the downloaded data + // to make a usable sfnt structure. + // This may cause aFontData to point to a new buffer, or be NULL. + aFontData = PrepareOpenTypeData(aFontData, &aLength); + if (aFontData && + gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) { + // Here ownership of aFontData is passed to the platform, + // which will delete it when no longer required + fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe, + aFontData, + aLength); + aFontData = nsnull; // the platform may have freed the data now! + } else { + // the data was unusable, so just discard it + // (error will be reported below, if logging is enabled) + if (aFontData) { + NS_Free((void*)aFontData); + } + } + if (fe) { static_cast(pe->mFamily)->ReplaceFontEntry(pe, fe); IncrementGeneration(); @@ -195,10 +271,9 @@ gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad, if (LOG_ENABLED()) { nsCAutoString fontURI; pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI); - - LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", - this, pe->mSrcIndex, fontURI.get(), - NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), + LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", + this, pe->mSrcIndex, fontURI.get(), + NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), PRUint32(mGeneration))); } #endif @@ -208,21 +283,24 @@ gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad, if (LOG_ENABLED()) { nsCAutoString fontURI; pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI); - LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n", - this, pe->mSrcIndex, fontURI.get(), + LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n", + this, pe->mSrcIndex, fontURI.get(), NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get())); } #endif } } else { // download failed + if (aFontData) { + NS_Free((void*)aFontData); + } #ifdef PR_LOGGING if (LOG_ENABLED()) { nsCAutoString fontURI; pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI); - LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error %8.8x downloading font data\n", - this, pe->mSrcIndex, fontURI.get(), - NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), + LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error %8.8x downloading font data\n", + this, pe->mSrcIndex, fontURI.get(), + NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), aDownloadStatus)); } #endif diff --git a/gfx/thebes/src/gfxWindowsFonts.cpp b/gfx/thebes/src/gfxWindowsFonts.cpp index b64cfd61a336..92a3f06c62e8 100644 --- a/gfx/thebes/src/gfxWindowsFonts.cpp +++ b/gfx/thebes/src/gfxWindowsFonts.cpp @@ -515,15 +515,14 @@ public: /* static */ FontEntry* FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry, - nsISupports *aLoader,const PRUint8 *aFontData, - PRUint32 aLength) { + const PRUint8 *aFontData, + PRUint32 aLength) +{ // if calls aren't available, bail if (!TTLoadEmbeddedFontPtr || !TTDeleteEmbeddedFontPtr) return nsnull; - PRBool isCFF; - if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength, &isCFF)) - return nsnull; + PRBool isCFF = gfxFontUtils::IsCffFont(aFontData); nsresult rv; HANDLE fontRef = nsnull; diff --git a/gfx/thebes/src/gfxWindowsPlatform.cpp b/gfx/thebes/src/gfxWindowsPlatform.cpp index 20c2ea033390..e8d3c43fff0a 100644 --- a/gfx/thebes/src/gfxWindowsPlatform.cpp +++ b/gfx/thebes/src/gfxWindowsPlatform.cpp @@ -841,13 +841,18 @@ gfxWindowsPlatform::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, gfxFontEntry* gfxWindowsPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - nsISupports *aLoader, const PRUint8 *aFontData, PRUint32 aLength) { #ifdef MOZ_FT2_FONTS - return FontEntry::CreateFontEntry(*aProxyEntry, aLoader, aFontData, aLength); + // The FT2 font needs the font data to persist, so we do NOT free it here + // but instead pass ownership to the font entry. + // Deallocation will happen later, when the font face is destroyed. + return FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength); #else - return FontEntry::LoadFont(*aProxyEntry, aLoader, aFontData, aLength); + // With GDI, we can free the downloaded data after activating the font + gfxFontEntry *fe = FontEntry::LoadFont(*aProxyEntry, aFontData, aLength); + NS_Free((void*)aFontData); + return fe; #endif } diff --git a/gfx/thebes/src/woff-private.h b/gfx/thebes/src/woff-private.h new file mode 100644 index 000000000000..cc78ae415c06 --- /dev/null +++ b/gfx/thebes/src/woff-private.h @@ -0,0 +1,159 @@ +/* -*- 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 + * + * 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') +#define TABLE_TAG_bhed TAG('b','h','e','d') + +#define SFNT_CHECKSUM_CALC_CONST 0xB1B0AFBAU /* from the TT/OT spec */ + +#ifdef WOFF_MOZILLA_CLIENT/* Copies of the NS_SWAP16 and NS_SWAP32 macros; they are defined in + a C++ header in mozilla, so we cannot include that here */ +# include "prtypes.h" /* defines IS_LITTLE_ENDIAN / IS_BIG_ENDIAN */ +# if defined IS_LITTLE_ENDIAN +# define READ16BE(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) +# define READ32BE(x) ((READ16BE((x) & 0xffff) << 16) | (READ16BE((x) >> 16))) +# elif defined IS_BIG_ENDIAN +# define READ16BE(x) (x) +# define READ32BE(x) (x) +# else +# error "Unknown byte order" +# endif +#else +/* 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] ) +#endif + +#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; + uint16_t numTables; + uint16_t reserved; + uint32_t totalSfntSize; + 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; + +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; + +#define HEAD_TABLE_SIZE 54 /* sizeof(sfntHeadTable) may report 56 because of alignment */ + +typedef struct { + uint32_t offset; + uint16_t oldIndex; + uint16_t newIndex; +} tableOrderRec; + +#pragma pack(pop) + +#endif diff --git a/gfx/thebes/src/woff.c b/gfx/thebes/src/woff.c new file mode 100644 index 000000000000..9f2967cb42ef --- /dev/null +++ b/gfx/thebes/src/woff.c @@ -0,0 +1,1170 @@ +/* -*- 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 + * + * 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 +#include +#include +#include + +#ifdef WOFF_MOZILLA_CLIENT /* define this when building as part of Gecko */ +# include "prmem.h" +# define malloc PR_Malloc +# define realloc PR_Realloc +# define free PR_Free +#endif + +/* + * Just simple whole-file encoding and decoding functions; a more extensive + * WOFF library could provide support for accessing individual tables from a + * compressed font, alternative options for memory allocation/ownership and + * error handling, etc. + */ + +/* on errors, each function sets a status variable and jumps to failure: */ +#undef FAIL +#define FAIL(err) do { status |= err; goto failure; } while (0) + +/* adjust an offset for longword alignment */ +#define LONGALIGN(x) (((x) + 3) & ~3) + +static int +compareOffsets(const void * lhs, const void * rhs) +{ + const tableOrderRec * a = (const tableOrderRec *) lhs; + const tableOrderRec * b = (const tableOrderRec *) rhs; + /* don't simply return a->offset - b->offset because these are unsigned + offset values; could convert to int, but possible integer overflow */ + return a->offset > b->offset ? 1 : + a->offset < b->offset ? -1 : + 0; +} + +#ifndef WOFF_MOZILLA_CLIENT + +/******************************************************************/ +/* * * * * * * * * * * * * * ENCODING * * * * * * * * * * * * * * */ +/******************************************************************/ + +static uint32_t +calcChecksum(const sfntDirEntry * dirEntry, + const uint8_t * sfntData, uint32_t sfntLen) +{ + /* just returns zero on errors, they will be detected again elsewhere */ + const uint32_t * csumPtr; + const uint32_t * csumEnd; + uint32_t csum = 0; + uint32_t length = LONGALIGN(READ32BE(dirEntry->length)); + uint32_t offset = READ32BE(dirEntry->offset); + uint32_t tag; + if ((offset & 3) != 0) { + return csum; + } + if (length > sfntLen || offset > sfntLen - length) { + return csum; + } + csumPtr = (const uint32_t *) (sfntData + offset); + csumEnd = csumPtr + length / 4; + while (csumPtr < csumEnd) { + csum += READ32BE(*csumPtr); + csumPtr++; + } + tag = READ32BE(dirEntry->tag); + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + const sfntHeadTable * head; + if (length < HEAD_TABLE_SIZE) { + return 0; + } + head = (const sfntHeadTable *)(sfntData + offset); + csum -= READ32BE(head->checkSumAdjustment); + } + return csum; +} + +const uint8_t * +woffEncode(const uint8_t * sfntData, uint32_t sfntLen, + uint16_t majorVersion, uint16_t minorVersion, + uint32_t * woffLen, uint32_t * pStatus) +{ + uint8_t * woffData = NULL; + tableOrderRec * tableOrder = NULL; + + uint32_t tableOffset; + uint32_t totalSfntSize; + + uint16_t numOrigTables; + uint16_t numTables; + uint16_t tableIndex; + uint16_t order; + const sfntDirEntry * sfntDir; + uint32_t tableBase; + uint32_t checkSumAdjustment = 0; + woffHeader * newHeader; + uint32_t tag = 0; + uint32_t removedDsigSize = 0; + uint32_t status = eWOFF_ok; + + const sfntHeader * header = (const sfntHeader *) (sfntData); + const sfntHeadTable * head = NULL; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (READ32BE(header->version) != SFNT_VERSION_TT && + READ32BE(header->version) != SFNT_VERSION_CFF && + READ32BE(header->version) != SFNT_VERSION_true) { + status |= eWOFF_warn_unknown_version; + } + + numOrigTables = READ16BE(header->numTables); + sfntDir = (const sfntDirEntry *) (sfntData + sizeof(sfntHeader)); + + for (tableIndex = 0; tableIndex < numOrigTables; ++tableIndex) { + /* validate table checksums, to figure out if we need to drop DSIG; + also check that table directory is correctly sorted */ + uint32_t prevTag = tag; + uint32_t csum = calcChecksum(&sfntDir[tableIndex], sfntData, sfntLen); + if (csum != READ32BE(sfntDir[tableIndex].checksum)) { + status |= eWOFF_warn_checksum_mismatch; + } + checkSumAdjustment += csum; + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag <= prevTag) { + FAIL(eWOFF_invalid); + } + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + if (READ32BE(sfntDir[tableIndex].length) < HEAD_TABLE_SIZE) { + FAIL(eWOFF_invalid); + } + head = (const sfntHeadTable *)(sfntData + + READ32BE(sfntDir[tableIndex].offset)); + } + } + if (!head) { + FAIL(eWOFF_invalid); + } + if ((status & eWOFF_warn_checksum_mismatch) == 0) { + /* no point even checking if we already have an error, + as fixing that will change the overall checksum too */ + const uint32_t * csumPtr = (const uint32_t *) sfntData; + const uint32_t * csumEnd = csumPtr + 3 + 4 * numOrigTables; + while (csumPtr < csumEnd) { + checkSumAdjustment += READ32BE(*csumPtr); + ++csumPtr; + } + checkSumAdjustment = 0xB1B0AFBA - checkSumAdjustment; + if (checkSumAdjustment != READ32BE(head->checkSumAdjustment)) { + status |= eWOFF_warn_checksum_mismatch; + } + } + + /* Fixing checkSumAdjustment is tricky, because if there's a DSIG table, + we're going to have to remove that, which in turn means that table + offsets in the directory will all change. + And recalculating checkSumAdjustment requires taking account of any + individual table checksum corrections, but they have not actually been + applied to the sfnt data at this point. + And finally, we'd need to get the corrected checkSumAdjustment into the + encoded head table (but we can't modify the original sfnt data). + An easier way out seems to be to go ahead and encode the font, knowing + that checkSumAdjustment will be wrong; then (if the status flag + eWOFF_warn_checksum_mismatch is set) we'll decode the font back to + sfnt format. This will fix up the checkSumAdjustment (and return a + warning status). We'll ignore that warning, and then re-encode the + new, cleaned-up sfnt to get the final WOFF data. Perhaps not the most + efficient approach, but it seems simpler than trying to predict the + correct final checkSumAdjustment and incorporate it into the head + table on the fly. */ + + tableOrder = (tableOrderRec *) malloc(numOrigTables * sizeof(tableOrderRec)); + if (!tableOrder) { + FAIL(eWOFF_out_of_memory); + } + for (tableIndex = 0, numTables = 0; + tableIndex < numOrigTables; ++tableIndex) { + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + /* check for DSIG table that we must drop if we're fixing checksums */ + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag == TABLE_TAG_DSIG) { + status |= eWOFF_warn_removed_DSIG; + removedDsigSize = READ32BE(sfntDir[tableIndex].length); + continue; + } + } + tableOrder[numTables].offset = READ32BE(sfntDir[tableIndex].offset); + tableOrder[numTables].oldIndex = tableIndex; + tableOrder[numTables].newIndex = numTables; + ++numTables; + } + qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets); + + /* initially, allocate space for header and directory */ + tableOffset = sizeof(woffHeader) + numTables * sizeof(woffDirEntry); + woffData = (uint8_t *) malloc(tableOffset); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + + /* accumulator for total expected size of decoded font */ + totalSfntSize = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry); + +/* + * We use a macro for this rather than creating a variable because woffData + * will get reallocated during encoding. The macro avoids the risk of using a + * stale pointer, and the compiler should optimize multiple successive uses. + */ +#define WOFFDIR ((woffDirEntry *) (woffData + sizeof(woffHeader))) + + for (order = 0; order < numTables; ++order) { + uLong sourceLen, destLen; + uint32_t sourceOffset; + + uint16_t oldIndex = tableOrder[order].oldIndex; + uint16_t newIndex = tableOrder[order].newIndex; + + WOFFDIR[newIndex].tag = sfntDir[oldIndex].tag; + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + uint32_t csum = calcChecksum(&sfntDir[oldIndex], sfntData, sfntLen); + WOFFDIR[newIndex].checksum = READ32BE(csum); + } else { + WOFFDIR[newIndex].checksum = sfntDir[oldIndex].checksum; + } + WOFFDIR[newIndex].origLen = sfntDir[oldIndex].length; + + /* we always realloc woffData to a long-aligned size, so this is safe */ + while ((tableOffset & 3) != 0) { + woffData[tableOffset++] = 0; + } + WOFFDIR[newIndex].offset = READ32BE(tableOffset); + + /* allocate enough space for upper bound of compressed size */ + sourceOffset = READ32BE(sfntDir[oldIndex].offset); + if ((sourceOffset & 3) != 0) { + status |= eWOFF_warn_misaligned_table; + } + sourceLen = READ32BE(sfntDir[oldIndex].length); + if (sourceLen > sfntLen || sourceOffset > sfntLen - sourceLen) { + FAIL(eWOFF_invalid); + } + destLen = LONGALIGN(compressBound(sourceLen)); + woffData = (uint8_t *) realloc(woffData, tableOffset + destLen); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + + /* do the compression directly into the WOFF data block */ + if (compress2((Bytef *) (woffData + tableOffset), &destLen, + (const Bytef *) (sfntData + sourceOffset), + sourceLen, 9) != Z_OK) { + FAIL(eWOFF_compression_failure); + } + if (destLen < sourceLen) { + /* compressed table was smaller */ + tableOffset += destLen; + WOFFDIR[newIndex].compLen = READ32BE(destLen); + } else { + /* compression didn't make it smaller, so store original data instead */ + destLen = sourceLen; + /* reallocate to ensure enough space for the table, + plus potential padding after it */ + woffData = (uint8_t *) realloc(woffData, + tableOffset + LONGALIGN(sourceLen)); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + /* copy the original data into place */ + memcpy(woffData + tableOffset, + sfntData + READ32BE(sfntDir[oldIndex].offset), sourceLen); + tableOffset += sourceLen; + WOFFDIR[newIndex].compLen = WOFFDIR[newIndex].origLen; + } + + /* update total size of uncompressed OpenType with table size */ + totalSfntSize += sourceLen; + totalSfntSize = LONGALIGN(totalSfntSize); + } + + if (totalSfntSize > sfntLen) { + if (totalSfntSize > LONGALIGN(sfntLen)) { + FAIL(eWOFF_invalid); + } else { + status |= eWOFF_warn_unpadded_table; + } + } else if (totalSfntSize < sfntLen) { + /* check if the remaining data is a DSIG we're removing; + if so, we're already warning about that */ + if ((status & eWOFF_warn_removed_DSIG) != 0 || + sfntLen - totalSfntSize > + LONGALIGN(removedDsigSize) + sizeof(sfntDirEntry)) { + status |= eWOFF_warn_trailing_data; + } + } + + /* write the header */ + newHeader = (woffHeader *) (woffData); + newHeader->signature = WOFF_SIGNATURE; + newHeader->signature = READ32BE(newHeader->signature); + newHeader->flavor = header->version; + newHeader->length = READ32BE(tableOffset); + newHeader->numTables = READ16BE(numTables); + newHeader->reserved = 0; + newHeader->totalSfntSize = READ32BE(totalSfntSize); + newHeader->majorVersion = READ16BE(majorVersion); + newHeader->minorVersion = READ16BE(minorVersion); + newHeader->metaOffset = 0; + newHeader->metaCompLen = 0; + newHeader->metaOrigLen = 0; + newHeader->privOffset = 0; + newHeader->privLen = 0; + + free(tableOrder); + + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + /* The original font had checksum errors, so we now decode our WOFF data + back to sfnt format (which fixes checkSumAdjustment), then re-encode + to get a clean copy. */ + const uint8_t * cleanSfnt = woffDecode(woffData, tableOffset, + &sfntLen, &status); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + free(woffData); + woffData = (uint8_t *) woffEncode(cleanSfnt, sfntLen, + majorVersion, minorVersion, + &tableOffset, &status); + free((void *) cleanSfnt); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + } + + if (woffLen) { + *woffLen = tableOffset; + } + if (pStatus) { + *pStatus |= status; + } + return woffData; + +failure: + if (tableOrder) { + free(tableOrder); + } + 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 * woffDir; + uint8_t * newData = NULL; + uint8_t * tableData = NULL; + woffHeader * newHeader; + uint16_t numTables; + uint32_t tableLimit, totalSize, offset; + uint16_t 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 = READ16BE(origHeader->numTables); + woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + tableLimit = 0; + for (i = 0; i < numTables; ++i) { + uint32_t end = READ32BE(woffDir[i].offset) + READ32BE(woffDir[i].compLen); + if (end > tableLimit) { + tableLimit = end; + } + } + tableLimit = LONGALIGN(tableLimit); + + /* check for broken input (meta/priv data before sfnt tables) */ + offset = READ32BE(origHeader->metaOffset); + if (offset != 0 && offset < tableLimit) { + FAIL(eWOFF_illegal_order); + } + offset = READ32BE(origHeader->privOffset); + if (offset != 0 && offset < tableLimit) { + FAIL(eWOFF_illegal_order); + } + + totalSize = tableLimit; /* already long-aligned */ + if (metaCompLen) { + totalSize += metaCompLen; + } + if (privLen) { + totalSize = LONGALIGN(totalSize) + privLen; + } + newData = malloc(totalSize); + if (!newData) { + FAIL(eWOFF_out_of_memory); + } + + /* copy the header, directory, and sfnt tables */ + memcpy(newData, woffData, tableLimit); + + /* then overwrite the header fields that should be changed */ + newHeader = (woffHeader *) newData; + newHeader->length = READ32BE(totalSize); + newHeader->metaOffset = 0; + newHeader->metaCompLen = 0; + newHeader->metaOrigLen = 0; + newHeader->privOffset = 0; + newHeader->privLen = 0; + + offset = tableLimit; + if (metaData && metaCompLen > 0 && metaOrigLen > 0) { + newHeader->metaOffset = READ32BE(offset); + newHeader->metaCompLen = READ32BE(metaCompLen); + newHeader->metaOrigLen = READ32BE(metaOrigLen); + memcpy(newData + offset, metaData, metaCompLen); + offset += metaCompLen; + } + + if (privData && privLen > 0) { + while ((offset & 3) != 0) { + newData[offset++] = 0; + } + newHeader->privOffset = READ32BE(offset); + newHeader->privLen = READ32BE(privLen); + memcpy(newData + offset, privData, privLen); + offset += privLen; + } + + *woffLen = offset; + free((void *) woffData); + + if (pStatus) { + *pStatus |= status; + } + return newData; + +failure: + if (newData) { + free(newData); + } + 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 = 0; + uint8_t * compData = NULL; + const uint8_t * privData = NULL; + uint32_t privLen = 0; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (!woffData || !woffLen) { + FAIL(eWOFF_bad_parameter); + } + + 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); + } + } + + if (metaData && metaLen > 0) { + 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 (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (!woffData || !woffLen) { + FAIL(eWOFF_bad_parameter); + } + + 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; +} + +#endif /* WOFF_MOZILLA_CLIENT */ + +/******************************************************************/ +/* * * * * * * * * * * * * * DECODING * * * * * * * * * * * * * * */ +/******************************************************************/ + +static uint32_t +sanityCheck(const uint8_t * woffData, uint32_t woffLen) +{ + const woffHeader * header; + uint16_t numTables, i; + const woffDirEntry * dirEntry; + uint32_t tableTotal = 0; + + if (!woffData || !woffLen) { + return eWOFF_bad_parameter; + } + + if (woffLen < sizeof(woffHeader)) { + return eWOFF_invalid; + } + + header = (const woffHeader *) (woffData); + if (READ32BE(header->signature) != WOFF_SIGNATURE) { + return eWOFF_bad_signature; + } + + if (READ32BE(header->length) != woffLen || header->reserved != 0) { + return eWOFF_invalid; + } + + numTables = READ16BE(header->numTables); + if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) { + return eWOFF_invalid; + } + + dirEntry = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + for (i = 0; i < numTables; ++i) { + uint32_t offs = READ32BE(dirEntry->offset); + uint32_t orig = READ32BE(dirEntry->origLen); + uint32_t comp = READ32BE(dirEntry->compLen); + if (comp > orig || comp > woffLen || offs > woffLen - comp) { + return eWOFF_invalid; + } + orig = (orig + 3) & ~3; + if (tableTotal > 0xffffffffU - orig) { + return eWOFF_invalid; + } + tableTotal += orig; + ++dirEntry; + } + + if (tableTotal > 0xffffffffU - sizeof(sfntHeader) - + numTables * sizeof(sfntDirEntry) || + READ32BE(header->totalSfntSize) != + tableTotal + sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry)) { + return eWOFF_invalid; + } + + return eWOFF_ok; +} + +uint32_t +woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen, + uint32_t * pStatus) +{ + uint32_t status = eWOFF_ok; + uint32_t totalLen = 0; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return 0; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + /* totalLen must be correctly rounded up to 4-byte alignment, otherwise + sanityCheck would have failed */ + +failure: + if (pStatus) { + *pStatus = status; + } + return totalLen; +} + +static void +woffDecodeToBufferInternal(const uint8_t * woffData, uint32_t woffLen, + uint8_t * sfntData, uint32_t bufferLen, + uint32_t * pActualSfntLen, uint32_t * pStatus) +{ + /* this is only called after sanityCheck has verified that + (a) basic header fields are ok + (b) all the WOFF table offset/length pairs are valid (within the data) + (c) the sum of original sizes + header/directory matches totalSfntSize + so we don't have to re-check those overflow conditions here */ + tableOrderRec * tableOrder = NULL; + const woffHeader * header; + uint16_t numTables; + uint16_t tableIndex; + uint16_t order; + const woffDirEntry * woffDir; + uint32_t totalLen; + 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 oldCheckSumAdjustment; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + /* check basic header fields */ + header = (const woffHeader *) (woffData); + 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 = READ16BE(header->numTables); + woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + + totalLen = READ32BE(header->totalSfntSize); + + /* 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); + + tableOrder = (tableOrderRec *) malloc(numTables * sizeof(tableOrderRec)); + if (!tableOrder) { + FAIL(eWOFF_out_of_memory); + } + for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { + tableOrder[tableIndex].offset = READ32BE(woffDir[tableIndex].offset); + tableOrder[tableIndex].oldIndex = tableIndex; + } + qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets); + + /* process each table, filling in the sfnt directory */ + offset = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry); + sfntDir = (sfntDirEntry *) (sfntData + sizeof(sfntHeader)); + for (order = 0; order < numTables; ++order) { + uint32_t origLen, compLen, tag, sourceOffset; + tableIndex = tableOrder[order].oldIndex; + + /* validity of these was confirmed by sanityCheck */ + origLen = READ32BE(woffDir[tableIndex].origLen); + compLen = READ32BE(woffDir[tableIndex].compLen); + sourceOffset = READ32BE(woffDir[tableIndex].offset); + + sfntDir[tableIndex].tag = woffDir[tableIndex].tag; + sfntDir[tableIndex].offset = READ32BE(offset); + sfntDir[tableIndex].length = woffDir[tableIndex].origLen; + sfntDir[tableIndex].checksum = woffDir[tableIndex].checksum; + csum += READ32BE(sfntDir[tableIndex].checksum); + + if (compLen < origLen) { + uLongf destLen = origLen; + if (uncompress((Bytef *)(sfntData + offset), &destLen, + (const Bytef *)(woffData + sourceOffset), + compLen) != Z_OK || destLen != origLen) { + FAIL(eWOFF_compression_failure); + } + } else { + memcpy(sfntData + offset, woffData + sourceOffset, origLen); + } + + /* note that old Mac bitmap-only fonts have no 'head' table + (eg NISC18030.ttf) but a 'bhed' table instead */ + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + headOffset = offset; + headLength = origLen; + } + + offset += origLen; + + while (offset < totalLen && (offset & 3) != 0) { + sfntData[offset++] = 0; + } + } + + if (headOffset > 0) { + /* 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 (headLength < HEAD_TABLE_SIZE) { + FAIL(eWOFF_invalid); + } + head = (sfntHeadTable *)(sfntData + headOffset); + oldCheckSumAdjustment = READ32BE(head->checkSumAdjustment); + head->checkSumAdjustment = 0; + csumPtr = (const uint32_t *)sfntData; + while (csumPtr < (const uint32_t *)(sfntData + sizeof(sfntHeader) + + numTables * sizeof(sfntDirEntry))) { + csum += READ32BE(*csumPtr); + csumPtr++; + } + csum = SFNT_CHECKSUM_CALC_CONST - csum; + + if (oldCheckSumAdjustment != csum) { + /* if the checksum doesn't match, we fix it; but this will invalidate + any DSIG that may be present */ + status |= eWOFF_warn_checksum_mismatch; + } + head->checkSumAdjustment = READ32BE(csum); + } + + if (pActualSfntLen) { + *pActualSfntLen = totalLen; + } + if (pStatus) { + *pStatus |= status; + } + free(tableOrder); + return; + +failure: + if (tableOrder) { + free(tableOrder); + } + if (pActualSfntLen) { + *pActualSfntLen = 0; + } + if (pStatus) { + *pStatus = status; + } +} + +void +woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen, + uint8_t * sfntData, uint32_t bufferLen, + uint32_t * pActualSfntLen, uint32_t * pStatus) +{ + uint32_t status = eWOFF_ok; + uint32_t totalLen; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + if (!sfntData) { + FAIL(eWOFF_bad_parameter); + } + + totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + if (bufferLen < totalLen) { + FAIL(eWOFF_buffer_too_small); + } + + woffDecodeToBufferInternal(woffData, woffLen, sfntData, bufferLen, + pActualSfntLen, pStatus); + 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; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + bufLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + sfntData = (uint8_t *) malloc(bufLen); + if (!sfntData) { + FAIL(eWOFF_out_of_memory); + } + + woffDecodeToBufferInternal(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; +} + +#ifndef WOFF_MOZILLA_CLIENT + +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 (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + header = (const woffHeader *) (woffData); + + offset = READ32BE(header->metaOffset); + compLen = READ32BE(header->metaCompLen); + origLen = READ32BE(header->metaOrigLen); + if (offset == 0 || compLen == 0 || origLen == 0) { + return NULL; + } + + if (compLen > woffLen || offset > woffLen - compLen) { + 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 (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + header = (const woffHeader *) (woffData); + + offset = READ32BE(header->privOffset); + length = READ32BE(header->privLen); + if (offset == 0 || length == 0) { + return NULL; + } + + if (length > woffLen || offset > woffLen - length) { + 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 status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + if (!major || !minor) { + FAIL(eWOFF_bad_parameter); + } + + *major = *minor = 0; + + header = (const woffHeader *) (woffData); + + *major = READ16BE(header->majorVersion); + *minor = READ16BE(header->minorVersion); + +failure: + if (pStatus) { + *pStatus = status; + } +} + +/* utility to print messages corresponding to WOFF encoder/decoder errors */ +void +woffPrintStatus(FILE * f, uint32_t status, const char * prefix) +{ + if (!prefix) { + prefix = ""; + } + if (WOFF_WARNING(status)) { + const char * template = "%sWOFF warning: %s\n"; + if (status & eWOFF_warn_unknown_version) { + fprintf(f, template, prefix, "unrecognized sfnt version"); + } + if (status & eWOFF_warn_checksum_mismatch) { + fprintf(f, template, prefix, "checksum mismatch (corrected)"); + } + if (status & eWOFF_warn_misaligned_table) { + fprintf(f, template, prefix, "misaligned font table"); + } + if (status & eWOFF_warn_trailing_data) { + fprintf(f, template, prefix, "extraneous input data discarded"); + } + if (status & eWOFF_warn_unpadded_table) { + fprintf(f, template, prefix, "final table not correctly padded"); + } + if (status & eWOFF_warn_removed_DSIG) { + fprintf(f, template, prefix, "digital signature (DSIG) table removed"); + } + } + if (WOFF_FAILURE(status)) { + const char * template = "%sWOFF error: %s\n"; + const char * msg; + switch (status & 0xff) { + case eWOFF_out_of_memory: + msg = "memory allocation failure"; + break; + case eWOFF_invalid: + msg = "invalid input font"; + break; + case eWOFF_compression_failure: + msg = "zlib compression/decompression failure"; + break; + case eWOFF_bad_signature: + msg = "incorrect WOFF file signature"; + break; + case eWOFF_buffer_too_small: + msg = "buffer too small"; + break; + case eWOFF_bad_parameter: + msg = "bad parameter to WOFF function"; + break; + case eWOFF_illegal_order: + msg = "incorrect table directory order"; + break; + default: + msg = "unknown internal error"; + break; + } + fprintf(f, template, prefix, msg); + } +} + +#endif /* not WOFF_MOZILLA_CLIENT */ diff --git a/gfx/thebes/src/woff.h b/gfx/thebes/src/woff.h new file mode 100644 index 000000000000..d8c6f55b0826 --- /dev/null +++ b/gfx/thebes/src/woff.h @@ -0,0 +1,211 @@ +/* -*- 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 + * + * 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 +#endif + +#include /* only for FILE, needed for woffPrintStatus */ + +/* 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 */ + eWOFF_bad_parameter = 6, /* bad parameter (e.g., null source ptr) */ + eWOFF_illegal_order = 7, /* improperly ordered chunks in WOFF font */ + + /* 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_checksum_mismatch = 0x0200, /* bad checksum, use with caution; + any DSIG will be invalid */ + eWOFF_warn_misaligned_table = 0x0400, /* table not long-aligned; fixing, + but DSIG will be invalid */ + eWOFF_warn_trailing_data = 0x0800, /* trailing junk discarded, + any DSIG may be invalid */ + eWOFF_warn_unpadded_table = 0x1000, /* sfnt not correctly padded, + any DSIG may be invalid */ + eWOFF_warn_removed_DSIG = 0x2000 /* removed digital signature + while fixing checksum errors */ +}; + +/* Note: status parameters must be initialized to eWOFF_ok before calling + WOFF functions. If the status parameter contains an error code, + functions will return immediately. */ + +#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); + + +/***************************************************************************** + * Utility to print warning and/or error status to the specified FILE*. + * The prefix string will be prepended to each line (ok to pass NULL if no + * prefix is wanted). + * (Provides terse English messages only, not intended for end-user display; + * user-friendly tools should map the status codes to their own messages.) + */ +void woffPrintStatus(FILE * f, uint32_t status, const char * prefix); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index 802cf9dd65fc..ff4514cb1f27 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -130,8 +130,11 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader, if (!userFontSet) { return aStatus; } - - PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry, aLoader, + + // The userFontSet is responsible for freeing the downloaded data + // (aString) when finished with it; the pointer is no longer valid + // after OnLoadComplete returns. + PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry, aString, aStringLen, aStatus); @@ -143,7 +146,7 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader, LOG(("fontdownloader (%p) reflow\n", this)); } - return aStatus; + return NS_SUCCESS_ADOPTED_DATA; } void