diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h index ca538d16c1..4363ecd1d0 100644 --- a/include/core/SkFontHost.h +++ b/include/core/SkFontHost.h @@ -176,6 +176,11 @@ public: /////////////////////////////////////////////////////////////////////////// + /** Retrieve information about the typeface needed for inclusion in a + PDF output device. Returns NULL if it is unable to find the font. + */ + static SkPDFTypefaceInfo* GetPDFTypefaceInfo(SkFontID); + /** Return the number of tables in the font */ static int CountTables(SkFontID); diff --git a/include/core/SkTScopedPtr.h b/include/core/SkTScopedPtr.h new file mode 100644 index 0000000000..1e5d4c43a0 --- /dev/null +++ b/include/core/SkTScopedPtr.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkTScopedPtr_DEFINED +#define SkTScopedPtr_DEFINED + +#include "SkTypes.h" + +/** \class SkTScopedPtr + A SkTScopedPtr is like a T*, except that the destructor of SkTScopedPtr + automatically deletes the pointer it holds (if any). That is, SkTScopedPtr + owns the T object that it points to. Like a T*, a SkTScopedPtr may hold + either NULL or a pointer to a T object. Also like T*, SkTScopedPtr is + thread-compatible, and once you dereference it, you get the threadsafety + guarantees of T. + + The size of a SkTScopedPtr is small: sizeof(SkTScopedPtr) == sizeof(T*) +*/ +template class SkTScopedPtr : SkNoncopyable { +public: + explicit SkTScopedPtr(T* o = NULL) : fObj(o) {} + ~SkTScopedPtr() { + enum { kTypeMustBeComplete = sizeof(T) }; + delete fObj; + } + + /** Delete the current object, if any. Then take ownership of the + passed object. + */ + void reset(T* o = NULL) { + if (o != fObj) { + enum { kTypeMustBeComplete = sizeof(T) }; + delete fObj; + fObj = o; + } + } + + /** Without deleting the current object, return it and forget about it. + Similar to calling get() and reset(), but the object is not deleted. + */ + T* release() { + T* retVal = fObj; + fObj = NULL; + return retVal; + } + + T& operator*() const { + SkASSERT(fObj != NULL); + return *fObj; + } + T* operator->() const { + SkASSERT(fObj != NULL); + return fObj; + } + T* get() const { return fObj; } + + bool operator==(T* o) const { return fObj == o; } + bool operator!=(T* o) const { return fObj != o; } + +private: + T* fObj; + + // Forbid comparison of SkTScopedPtr types. If T2 != T, it doesn't make + // sense, and if T2 == T, it still doesn't make sense because the same + // object can't be owned by two different scoped_ptrs. + template bool operator==(SkTScopedPtr const& o2) const; + template bool operator!=(SkTScopedPtr const& o2) const; +}; + +#endif diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h index 27ebd416df..eaa812fdbd 100644 --- a/include/core/SkTemplates.h +++ b/include/core/SkTemplates.h @@ -59,6 +59,7 @@ private: T* fObj; }; +// See also SkTScopedPtr. template class SkAutoTDelete : SkNoncopyable { public: SkAutoTDelete(T* obj) : fObj(obj) {} diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index d4af700b34..b7ccf512e1 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -20,6 +20,7 @@ #include "SkRefCnt.h" class SkStream; +class SkPDFTypefaceInfo; class SkWStream; /** \class SkTypeface @@ -130,6 +131,11 @@ public: */ static SkTypeface* Deserialize(SkStream*); + /** Retrieve information about the typeface needed for inclusion in a + PDF output device. + */ + SkPDFTypefaceInfo* getPDFTypefaceInfo() const; + protected: /** uniqueID must be unique (please!) and non-zero */ diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h index 3ef234a0fe..64e0407855 100644 --- a/include/pdf/SkPDFDevice.h +++ b/include/pdf/SkPDFDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -143,13 +143,14 @@ private: // one entry: a transform // one entry: a clip // two entries: a clip and then a transform + // Pointers are owned by the respective Resources list. struct GraphicStackEntry { SkColor fColor; SkScalar fTextSize; SkScalar fTextScaleX; SkPaint::Style fTextFill; - SkRefPtr fFont; - SkRefPtr fGraphicState; + SkPDFFont* fFont; + SkPDFGraphicState* fGraphicState; SkRegion fClip; SkMatrix fTransform; }; @@ -159,7 +160,8 @@ private: SkString fContent; void updateGSFromPaint(const SkPaint& newPaint, bool forText); - int getFontResourceIndex(uint32_t fontID); + void updateFont(const SkPaint& paint, uint16_t glyphID); + int getFontResourceIndex(uint32_t fontID, uint16_t glyphID); void moveTo(SkScalar x, SkScalar y); void appendLine(SkScalar x, SkScalar y); diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h index 82357ef355..d020163189 100644 --- a/include/pdf/SkPDFFont.h +++ b/include/pdf/SkPDFFont.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include "SkThread.h" class SkPaint; +class SkPDFTypefaceInfo; /** \class SkPDFFont A PDF Object class representing a font. The font may have resources @@ -40,53 +41,95 @@ public: */ uint32_t fontID(); - /** Returns true if this font supports glyph IDs above 255. + /** Return true if this font has an encoding for the passed glyph id. + */ + bool hasGlyph(uint16_t glyphID); + + /** Returns true if this font encoding supports glyph IDs above 255. */ bool multiByteGlyphs(); - /** Covert the specified text to glyph IDs, taking into consideration - * PDF encodings and return the number of glyphs IDs written. + /** Convert the input glyph IDs into the font encoding. If the font has + * more glyphs than can be encoded (like a type 1 font with more than + * 255 glyphs) this method only converts up to the first out of range + * glyph ID. + * @param glyphIDs The input text as glyph IDs. + * @param numGlyphs The number of input glyphs. + * @param encodedValues The method writes its result to this parameter. + * multiByteGlyphs() reveals the output format. + * @param encodedLength The size of encodedValues (in bytes), which is + * overwritten with how much of encodedValues is + * used. + * @return Returns the number of glyphs consumed. */ - int textToPDFGlyphs(const void* text, size_t byteLength, - const SkPaint& paint, uint16_t* glyphs, - size_t glyphsLength) const; + size_t glyphsToPDFFontEncoding(const uint16_t* glyphIDs, size_t numGlyphs, + void* encodedValues, size_t* encodedLength); - /** Get the font resource for the passed font ID. The reference count of - * the object is incremented and it is the caller's responsibility to - * unreference it when done. This is needed to accommodate the weak - * reference pattern used when the returned object is new and has no - * other references. - * @param paint The SkPaint to emulate. + /** Get the font resource for the passed font ID and glyphID. The + * reference count of the object is incremented and it is the caller's + * responsibility to unreference it when done. This is needed to + * accommodate the weak reference pattern used when the returned object + * is new and has no other references. + * @param fontID The fontId to find. + * @param glyphID Specify which section of a large font is of interest. */ - static SkPDFFont* getFontResouceByID(uint32_t fontID); + static SkPDFFont* getFontResource(uint32_t fontID, uint16_t glyphID); private: uint32_t fFontID; +#ifdef SK_DEBUG + bool fDescendant; +#endif bool fMultiByteGlyphs; + // The glyph IDs accessible with this font. For Type1 (non CID) fonts, + // this will be a subset if the font has more than 255 glyphs. + uint16_t fFirstGlyphID; + uint16_t fLastGlyphID; + // The font info is only kept around after construction for large + // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs. + SkRefPtr fFontInfo; SkTDArray fResources; + SkRefPtr fDescriptor; class FontRec { public: SkPDFFont* fFont; uint32_t fFontID; + uint16_t fGlyphID; + // A fGlyphID of 0 with no fFont always matches. bool operator==(const FontRec& b) const; - FontRec(SkPDFFont* font, uint32_t fontID); + FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID); }; // This should be made a hash table if performance is a problem. static SkTDArray& canonicalFonts(); static SkMutex& canonicalFontsMutex(); - SkPDFFont(uint32_t fontID, bool multiByteGlyphs); + /** Construct a new font dictionary and support objects. + * @param fontInfo Information about the to create. + * @param fontID The font ID of the font. + * @param glyphID The glyph ID the caller is interested in. This + * is important only for Type1 fonts, which have + * more than 255 glyphs. + * @param descendantFont If this is the descendant (true) or root + * (Type 0 font - false) font dictionary. Only True + * Type and CID encoded fonts will use a true value. + * @param fontDescriptor If the font descriptor has already have generated + * for this font, pass it in here, otherwise pass + * NULL. + */ + SkPDFFont(class SkPDFTypefaceInfo* fontInfo, uint32_t fontID, + uint16_t glyphID, bool descendantFont, SkPDFDict* fontDescriptor); - void populateBuiltinFont(const char fontName[]); - void populateFont(const char subType[], const char fontName[], - int firstChar, int lastChar, int widths[], - SkPDFObject* fontDescriptor); + void populateType0Font(); + void populateCIDFont(); + bool populateType1Font(uint16_t firstGlyphID, uint16_t lastGlyphID); + void populateType3Font(); + bool addFontDescriptor(int defaultWidth); - static int find(uint32_t fontID); + static bool find(uint32_t fontID, uint16_t glyphID, int* index); }; #endif diff --git a/include/pdf/SkPDFTypefaceInfo.h b/include/pdf/SkPDFTypefaceInfo.h new file mode 100644 index 0000000000..2655d14abf --- /dev/null +++ b/include/pdf/SkPDFTypefaceInfo.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFTypefaceInfo_DEFINED +#define SkPDFTypefaceInfo_DEFINED + +#include "SkRect.h" +#include "SkRefCnt.h" +#include "SkString.h" +#include "SkTDArray.h" +#include "SkTemplates.h" +#include "SkTScopedPtr.h" + +/** \class SkPDFTypefaceInfo + + The SkPDFTypefaceInfo is used by the PDF backend to correctly embed + typefaces. This class is filled in with information about a given typeface + by the SkFontHost class. +*/ + +class SkPDFTypefaceInfo : public SkRefCnt { +public: + enum FontType { + kType1_Font, + kType1CID_Font, + kCFF_Font, + kTrueType_Font, + kOther_Font, + kNotEmbeddable_Font, + }; + // The type of the underlying font program. This field determines which + // of the following fields are valid. If it is kOther_Font or + // kNotEmbeddable_Font, fFontName may be valid, but no other fields are + // valid. + FontType fType; + + // fMultiMaster may be true for Type1_Font or CFF_Font. + bool fMultiMaster; + SkString fFontName; + SkIRect fBBox; // The bounding box of all glyphs (in font units). + + uint16_t fLastGlyphID; + + template + struct AdvanceMetric { + enum MetricType { + kDefault, // Default advance: fAdvance.count = 1 + kRange, // Advances for a range: fAdvance.count = fEndID-fStartID + kRun, // fStartID-fEndID have same advance: fAdvance.count = 1 + }; + MetricType fType; + int fStartId; + int fEndId; + SkTDArray fAdvance; + SkTScopedPtr > fNext; + }; + + struct VerticalMetric { + int fVerticalAdvance; + int fOriginXDisp; // Horizontal displacement of the secondary origin. + int fOriginYDisp; // Vertical displacement of the secondary origin. + }; + typedef struct VerticalMetric VerticalMetric; + typedef AdvanceMetric WidthRange; + typedef AdvanceMetric VerticalAdvanceRange; + + // This is indexed by glyph id. + SkTScopedPtr fGlyphWidths; + // Only used for Vertical CID fonts. + SkTScopedPtr fVerticalMetrics; + + // The names of each glyph, only populated for postscript fonts. + SkTScopedPtr > fGlyphNames; + + // Metrics: probably used by the pdf renderer for substitution, which + // shouldn't be needed with embedding fonts. Optional fields with a value + // of 0 will be ignored. + + // The enum values match the values used in the PDF file format. + enum StyleFlags { + kFixedPitch_Style = 0x00001, + kSerif_Style = 0x00002, + kSymbolic_Style = 0x00004, + kScript_Style = 0x00008, + kNonsymbolic_Style = 0x00020, + kItalic_Style = 0x00040, + kAllCaps_Style = 0x10000, + kSmallCaps_Style = 0x20000, + kForceBold_Style = 0x40000, + }; + uint16_t fStyle; // Font style characteristics. + long fItalicAngle; // Counterclockwise degrees from vertical of the + // dominant vertical stroke for an Italic face. + SkScalar fAscent; // Max height above baseline, not including accents. + SkScalar fDescent; // Max depth below baseline (negative). + SkScalar fStemV; // Thickness of dominant vertical stem. + SkScalar fCapHeight; // Height (from baseline) of top of flat capitals. + + /* Omit the optional entries. Better to let the viewer compute them, since + * it has to be able to anyway. + SkScalar fLeading; // Space between lines. Optional. + SkScalar fXHeight; // Height of top of 'x'. Optional. + SkScalar fStemH; // Thickness of dominant horizontal stem. Optional. + SkScalar fAvgWidth; // Average width of glyphs. Optional. + SkScalar fMaxWidth; // Max width of a glyph. Optional. + */ +}; + +#endif diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 257df278ea..6306098974 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "SkTypeface.h" #include "SkFontHost.h" @@ -59,4 +75,6 @@ SkTypeface* SkTypeface::Deserialize(SkStream* stream) { return SkFontHost::Deserialize(stream); } - +SkPDFTypefaceInfo* SkTypeface::getPDFTypefaceInfo() const { + return SkFontHost::GetPDFTypefaceInfo(fUniqueID); +} diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index fec9a4f883..fd458e0ea9 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -139,6 +139,8 @@ SkPDFDevice::SkPDFDevice(int width, int height) fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value. fGraphicStack[0].fTextScaleX = SK_Scalar1; fGraphicStack[0].fTextFill = SkPaint::kFill_Style; + fGraphicStack[0].fFont = NULL; + fGraphicStack[0].fGraphicState = NULL; fGraphicStack[0].fClip.setRect(0,0, width, height); fGraphicStack[0].fTransform.reset(); } @@ -158,17 +160,19 @@ void SkPDFDevice::setMatrixClip(const SkMatrix& matrix, pushGS(); SkPath clipPath; - if (!region.getBoundaryPath(&clipPath)) - clipPath.moveTo(SkIntToScalar(-1), SkIntToScalar(-1)); - emitPath(clipPath); + if (region.getBoundaryPath(&clipPath)) { + emitPath(clipPath); - SkPath::FillType clipFill = clipPath.getFillType(); - NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); - NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); - if (clipFill == SkPath::kEvenOdd_FillType) - fContent.append("W* n "); - else - fContent.append("W n "); + SkPath::FillType clipFill = clipPath.getFillType(); + NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, + false); + NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, + false); + if (clipFill == SkPath::kEvenOdd_FillType) + fContent.append("W* n "); + else + fContent.append("W n "); + } fGraphicStack[fGraphicStackIndex].fClip = region; } @@ -298,12 +302,25 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) { SkPaint textPaint = calculateTextPaint(paint); updateGSFromPaint(textPaint, true); - SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont.get(); - uint16_t glyphs[len]; - size_t glyphsLength; - glyphsLength = font->textToPDFGlyphs(text, len, textPaint, glyphs, len); - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + // Make sure we have a glyph id encoding. + SkAutoFree glyphStorage; + uint16_t* glyphIDs; + size_t numGlyphs; + if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { + numGlyphs = paint.textToGlyphs(text, len, NULL); + glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, + SK_MALLOC_TEMP | SK_MALLOC_THROW); + glyphStorage.set(glyphIDs); + paint.textToGlyphs(text, len, glyphIDs); + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + } else { + SkASSERT((len & 1) == 0); + numGlyphs = len / 2; + glyphIDs = (uint16_t*)text; + } + SkAutoFree encodedStorage( + sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW)); SkScalar width; SkScalar* widthPtr = NULL; @@ -311,13 +328,26 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, widthPtr = &width; SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); - alignText(glyphCacheProc, textPaint, glyphs, glyphsLength, &x, &y, - widthPtr); + alignText(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, widthPtr); fContent.append("BT\n"); setTextTransform(x, y, textPaint.getTextSkewX()); - fContent.append(SkPDFString::formatString(glyphs, glyphsLength, - font->multiByteGlyphs())); - fContent.append(" Tj\nET\n"); + size_t consumedGlyphCount = 0; + while (numGlyphs > consumedGlyphCount) { + updateFont(textPaint, glyphIDs[consumedGlyphCount]); + SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont; + size_t encodedLength = numGlyphs * 2; + consumedGlyphCount += font->glyphsToPDFFontEncoding( + glyphIDs + consumedGlyphCount, numGlyphs - consumedGlyphCount, + encodedStorage.get(), &encodedLength); + if (font->multiByteGlyphs()) + encodedLength /= 2; + fContent.append( + SkPDFString::formatString((const uint16_t*)encodedStorage.get(), + encodedLength, + font->multiByteGlyphs())); + fContent.append(" Tj\n"); + } + fContent.append("ET\n"); // Draw underline and/or strikethrough if the paint has them. // drawPosText() and drawTextOnPath() don't draw underline or strikethrough @@ -346,21 +376,42 @@ void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len, SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); SkPaint textPaint = calculateTextPaint(paint); updateGSFromPaint(textPaint, true); - SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont.get(); - uint16_t glyphs[len]; - size_t glyphsLength; - glyphsLength = font->textToPDFGlyphs(text, len, textPaint, glyphs, len); - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + // Make sure we have a glyph id encoding. + SkAutoFree glyphStorage; + uint16_t* glyphIDs; + size_t numGlyphs; + if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { + numGlyphs = paint.textToGlyphs(text, len, NULL); + glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, + SK_MALLOC_TEMP | SK_MALLOC_THROW); + glyphStorage.set(glyphIDs); + paint.textToGlyphs(text, len, glyphIDs); + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + } else { + SkASSERT((len & 1) == 0); + numGlyphs = len / 2; + glyphIDs = (uint16_t*)text; + } SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); fContent.append("BT\n"); - for (size_t i = 0; i < glyphsLength; i++) { + updateFont(textPaint, glyphIDs[0]); + for (size_t i = 0; i < numGlyphs; i++) { + SkPDFFont* font = fGraphicStack[fGraphicStackIndex].fFont; + uint16_t encodedValue; + size_t encodedLength = 2; + if (font->glyphsToPDFFontEncoding(glyphIDs + i, 1, &encodedValue, + &encodedLength) == 0) { + updateFont(textPaint, glyphIDs[i]); + i--; + continue; + } SkScalar x = pos[i * scalarsPerPos]; SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; - alignText(glyphCacheProc, textPaint, glyphs + i, 1, &x, &y, NULL); + alignText(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL); setTextTransform(x, y, textPaint.getTextSkewX()); - fContent.append(SkPDFString::formatString(glyphs + i, 1, + fContent.append(SkPDFString::formatString(&encodedValue, 1, font->multiByteGlyphs())); fContent.append(" Tj\n"); } @@ -539,7 +590,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) { newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref. // newGraphicState has been canonicalized so we can directly compare // pointers. - if (fGraphicStack[fGraphicStackIndex].fGraphicState.get() != + if (fGraphicStack[fGraphicStackIndex].fGraphicState != newGraphicState.get()) { int resourceIndex = fGraphicStateResources.find(newGraphicState.get()); if (resourceIndex < 0) { @@ -550,7 +601,7 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) { fContent.append("/G"); fContent.appendS32(resourceIndex); fContent.append(" gs\n"); - fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState; + fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get(); } SkColor newColor = newPaint.getColor(); @@ -565,22 +616,6 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) { } if (forText) { - uint32_t fontID = SkTypeface::UniqueID(newPaint.getTypeface()); - if (fGraphicStack[fGraphicStackIndex].fTextSize != - newPaint.getTextSize() || - fGraphicStack[fGraphicStackIndex].fFont.get() == NULL || - fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID) { - int fontIndex = getFontResourceIndex(fontID); - fContent.append("/F"); - fContent.appendS32(fontIndex); - fContent.append(" "); - fContent.appendScalar(newPaint.getTextSize()); - fContent.append(" Tf\n"); - fGraphicStack[fGraphicStackIndex].fTextSize = - newPaint.getTextSize(); - fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex]; - } - if (fGraphicStack[fGraphicStackIndex].fTextScaleX != newPaint.getTextScaleX()) { SkScalar scale = newPaint.getTextScaleX(); @@ -604,9 +639,27 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) { } } -int SkPDFDevice::getFontResourceIndex(uint32_t fontID) { - SkRefPtr newFont = SkPDFFont::getFontResouceByID(fontID); - newFont->unref(); // getFontResourceByID and SkRefPtr both took a ref. +void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) { + uint32_t fontID = SkTypeface::UniqueID(paint.getTypeface()); + if (fGraphicStack[fGraphicStackIndex].fTextSize != paint.getTextSize() || + fGraphicStack[fGraphicStackIndex].fFont == NULL || + fGraphicStack[fGraphicStackIndex].fFont->fontID() != fontID || + !fGraphicStack[fGraphicStackIndex].fFont->hasGlyph(glyphID)) { + int fontIndex = getFontResourceIndex(fontID, glyphID); + fContent.append("/F"); + fContent.appendS32(fontIndex); + fContent.append(" "); + fContent.appendScalar(paint.getTextSize()); + fContent.append(" Tf\n"); + fGraphicStack[fGraphicStackIndex].fTextSize = paint.getTextSize(); + fGraphicStack[fGraphicStackIndex].fFont = fFontResources[fontIndex]; + } +} + + +int SkPDFDevice::getFontResourceIndex(uint32_t fontID, uint16_t glyphID) { + SkRefPtr newFont = SkPDFFont::getFontResource(fontID, glyphID); + newFont->unref(); // getFontResource and SkRefPtr both took a ref. int resourceIndex = fFontResources.find(newFont.get()); if (resourceIndex < 0) { resourceIndex = fFontResources.count(); diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp index 2b1e3f731b..77a9fdbb61 100644 --- a/src/pdf/SkPDFDocument.cpp +++ b/src/pdf/SkPDFDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,12 +27,9 @@ void addResourcesToCatalog(int firstIndex, bool firstPage, for (int i = firstIndex; i < resourceList->count(); i++) { int index = resourceList->find((*resourceList)[i]); if (index != i) { - // The resource lists themselves should already be unique, so the - // first page resources shouldn't have any dups (assuming the first - // page resources are handled first). - SkASSERT(!firstPage); (*resourceList)[i]->unref(); resourceList->removeShuffle(i); + i--; } else { catalog->addObject((*resourceList)[i], firstPage); } @@ -68,6 +65,23 @@ bool SkPDFDocument::emitPDF(SkWStream* stream) { SkRefPtr pageTreeRootRef = new SkPDFObjRef(pageTreeRoot); pageTreeRootRef->unref(); // SkRefPtr and new both took a reference. fDocCatalog->insert("Pages", pageTreeRootRef.get()); + + /* TODO(vandebo) output intent + SkRefPtr outputIntent = new SkPDFDict("OutputIntent"); + outputIntent->unref(); // SkRefPtr and new both took a reference. + SkRefPtr intentSubtype = new SkPDFName("GTS_PDFA1"); + intentSubtype->unref(); // SkRefPtr and new both took a reference. + outputIntent->insert("S", intentSubtype.get()); + SkRefPtr intentIdentifier = new SkPDFString("sRGB"); + intentIdentifier->unref(); // SkRefPtr and new both took a reference. + outputIntent->insert("OutputConditionIdentifier", + intentIdentifier.get()); + SkRefPtr intentArray = new SkPDFArray; + intentArray->unref(); // SkRefPtr and new both took a reference. + intentArray->append(outputIntent.get()); + fDocCatalog->insert("OutputIntent", intentArray.get()); + */ + bool first_page = true; for (int i = 0; i < fPages.count(); i++) { int resourceCount = fPageResources.count(); diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp index 1bbc53af4e..e39530c685 100644 --- a/src/pdf/SkPDFFont.cpp +++ b/src/pdf/SkPDFFont.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,30 +14,296 @@ * limitations under the License. */ +#include + +#include "SkFontHost.h" #include "SkPaint.h" #include "SkPDFFont.h" +#include "SkPDFStream.h" +#include "SkPDFTypefaceInfo.h" +#include "SkPDFTypes.h" +#include "SkStream.h" +#include "SkTypeface.h" #include "SkUtils.h" namespace { -uint16_t unicharToWinAnsiGlyphID(SkUnichar uniChar) { - // TODO(vandebo) Quick hack to get text working. Either finish the - // implementation of this, or remove it and use the encoding built into - // the real font. - if (uniChar < ' ') - return 0; - if (uniChar < '~') - return uniChar; - return 0; +bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType, + size_t* size) { + // PFB sections have a two or six bytes header. 0x80 and a one byte + // section type followed by a four byte section length. Type one is + // an ASCII section (includes a length), type two is a binary section + // (includes a length) and type three is an EOF marker with no length. + const uint8_t* buf = *src; + if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) + return false; + if (buf[1] == 3) + return true; + if (*len < 6) + return false; + + *size = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24); + size_t consumed = *size + 6; + if (consumed > *len) + return false; + *src = *src + consumed; + *len = *len - consumed; + return true; } +bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + const uint8_t* srcPtr = src; + size_t remaining = size; + + return parsePFBSection(&srcPtr, &remaining, 1, headerLen) && + parsePFBSection(&srcPtr, &remaining, 2, dataLen) && + parsePFBSection(&srcPtr, &remaining, 1, trailerLen) && + parsePFBSection(&srcPtr, &remaining, 3, NULL); } +/* The sections of a PFA file are implicitly defined. The body starts + * after the line containing "eexec," and the trailer starts with 512 + * literal 0's followed by "cleartomark" (plus arbitrary white space). + * + * This function assumes that src is NUL terminated, but the NUL + * termination is not included in size. + * + */ +bool parsePFA(const char* src, size_t size, size_t* headerLen, + size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) { + const char* end = src + size; + + const char* dataPos = strstr(src, "eexec"); + if (!dataPos) + return false; + dataPos += strlen("eexec"); + while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') && + dataPos < end) + dataPos++; + *headerLen = dataPos - src; + + const char* trailerPos = strstr(dataPos, "cleartomark"); + if (!trailerPos) + return false; + int zeroCount = 0; + for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) { + if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') { + continue; + } else if (*trailerPos == '0') { + zeroCount++; + } else { + return false; + } + } + if (zeroCount != 512) + return false; + + *hexDataLen = trailerPos - src - *headerLen; + *trailerLen = size - *headerLen - *hexDataLen; + + // Verify that the data section is hex encoded and count the bytes. + int nibbles = 0; + for (; dataPos < trailerPos; dataPos++) { + if (isspace(*dataPos)) + continue; + if (!isxdigit(*dataPos)) + return false; + nibbles++; + } + *dataLen = (nibbles + 1) / 2; + + return true; +} + +int8_t hexToBin(uint8_t c) { + if (!isxdigit(c)) + return -1; + if (c <= '9') return c - '0'; + if (c <= 'F') return c - 'A' + 10; + if (c <= 'f') return c - 'a' + 10; + return -1; +} + +SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + // srcStream may be backed by a file or a unseekable fd, so we may not be + // able to use skip(), rewind(), or getMemoryBase(). read()ing through + // the input only once is doable, but very ugly. Furthermore, it'd be nice + // if the data was NUL terminated so that we can use strstr() to search it. + // Make as few copies as possible given these constraints. + SkDynamicMemoryWStream dynamicStream; + SkRefPtr staticStream; + const uint8_t* src; + size_t srcLen; + if ((srcLen = srcStream->getLength()) > 0) { + staticStream = new SkMemoryStream(srcLen + 1); + staticStream->unref(); // new and SkRefPtr both took a ref. + src = (const uint8_t*)staticStream->getMemoryBase(); + if (srcStream->getMemoryBase() != NULL) { + memcpy((void *)src, srcStream->getMemoryBase(), srcLen); + } else { + size_t read = 0; + while (read < srcLen) { + size_t got = srcStream->read((void *)staticStream->getAtPos(), + srcLen - read); + if (got == 0) + return NULL; + read += got; + staticStream->seek(read); + } + } + ((uint8_t *)src)[srcLen] = 0; + } else { + static const size_t bufSize = 4096; + uint8_t buf[bufSize]; + size_t amount; + while ((amount = srcStream->read(buf, bufSize)) > 0) + dynamicStream.write(buf, amount); + amount = 0; + dynamicStream.write(&amount, 1); // NULL terminator. + // getStream makes another copy, but we couldn't do any better. + src = (const uint8_t*)dynamicStream.getStream(); + srcLen = dynamicStream.getOffset() - 1; + } + + if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src + 6, *headerLen); + result->seek(*headerLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen); + result->seek(*headerLen + *dataLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen, + *trailerLen); + result->rewind(); + return result; + } + + // A PFA has to be converted for PDF. + size_t hexDataLen; + if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen, + trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src, *headerLen); + result->seek(*headerLen); + + const uint8_t* hexData = src + *headerLen; + const uint8_t* trailer = hexData + hexDataLen; + size_t outputOffset = 0; + uint8_t dataByte; + bool highNibble = true; + for (; hexData < trailer; hexData++) { + char curNibble = hexToBin(*hexData); + if (curNibble < 0) + continue; + if (highNibble) { + dataByte = curNibble << 4; + highNibble = false; + } else { + dataByte |= curNibble; + highNibble = true; + ((char *)result->getAtPos())[outputOffset++] = dataByte; + } + } + if (!highNibble) + ((char *)result->getAtPos())[outputOffset++] = dataByte; + SkASSERT(outputOffset == *dataLen); + result->seek(*headerLen + outputOffset); + + memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen, + *trailerLen); + result->rewind(); + return result; + } + + return NULL; +} + +void appendWidth(const int& width, SkPDFArray* array) { + SkRefPtr widthInt = new SkPDFInt(width); + widthInt->unref(); // SkRefPtr and new both took a reference. + array->append(widthInt.get()); +} + +void appendVerticalAdvance(const SkPDFTypefaceInfo::VerticalMetric& advance, + SkPDFArray* array) { + appendWidth(advance.fVerticalAdvance, array); + appendWidth(advance.fOriginXDisp, array); + appendWidth(advance.fOriginYDisp, array); +} + +template +SkPDFArray* composeAdvanceData( + SkPDFTypefaceInfo::AdvanceMetric* advanceInfo, + void (*appendAdvance)(const Data& advance, SkPDFArray* array), + Data* defaultAdvance) { + SkPDFArray* result = new SkPDFArray(); + for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) { + switch (advanceInfo->fType) { + case SkPDFTypefaceInfo::WidthRange::kDefault: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + *defaultAdvance = advanceInfo->fAdvance[0]; + break; + } + case SkPDFTypefaceInfo::WidthRange::kRange: { + SkRefPtr advanceArray = new SkPDFArray(); + advanceArray->unref(); // SkRefPtr and new both took a ref. + for (int j = 0; j < advanceInfo->fAdvance.count(); j++) + appendAdvance(advanceInfo->fAdvance[j], advanceArray.get()); + SkRefPtr rangeStart = + new SkPDFInt(advanceInfo->fStartId); + rangeStart->unref(); // SkRefPtr and new both took a reference. + result->append(rangeStart.get()); + result->append(advanceArray.get()); + break; + } + case SkPDFTypefaceInfo::WidthRange::kRun: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + SkRefPtr rangeStart = + new SkPDFInt(advanceInfo->fStartId); + rangeStart->unref(); // SkRefPtr and new both took a reference. + result->append(rangeStart.get()); + + SkRefPtr rangeEnd = new SkPDFInt(advanceInfo->fEndId); + rangeEnd->unref(); // SkRefPtr and new both took a reference. + result->append(rangeEnd.get()); + + appendAdvance(advanceInfo->fAdvance[0], result); + break; + } + } + } + return result; +} + +} // namespace + +/* Font subset design: It would be nice to be able to subset fonts + * (particularly type 3 fonts), but it's a lot of work and not a priority. + * + * Resources are canonicalized and uniqueified by pointer so there has to be + * some additional state indicating which subset of the font is used. It + * must be maintained at the page granularity and then combined at the document + * granularity. a) change SkPDFFont to fill in its state on demand, kind of + * like SkPDFGraphicState. b) maintain a per font glyph usage class in each + * page/pdf device. c) in the document, retrieve the per font glyph usage + * from each page and combine it and ask for a resource with that subset. + */ + SkPDFFont::~SkPDFFont() { SkAutoMutexAcquire lock(canonicalFontsMutex()); - int index = find(fFontID); - SkASSERT(index >= 0); - canonicalFonts().removeShuffle(index); + int index; + if (find(fFontID, fFirstGlyphID, &index)) { + canonicalFonts().removeShuffle(index); +#ifdef SK_DEBUG + SkASSERT(!fDescendant); + } else { + SkASSERT(fDescendant); +#endif + } + fResources.unrefAll(); } void SkPDFFont::getResources(SkTDArray* resourceList) { @@ -53,75 +319,68 @@ uint32_t SkPDFFont::fontID() { return fFontID; } +bool SkPDFFont::hasGlyph(uint16_t id) { + return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0; +} + bool SkPDFFont::multiByteGlyphs() { return fMultiByteGlyphs; } -// TODO(vandebo) This function goes or stays with unicharToWinAnsiGlyphID. -int SkPDFFont::textToPDFGlyphs(const void* text, size_t byteLength, - const SkPaint& paint, uint16_t glyphs[], - size_t glyphsLength) const { - // For a regular, non built-in font, just ask the paint. - if (fResources.count() != 0) { - SkASSERT(glyphsLength >= byteLength / 2); - if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { - memcpy(glyphs, text, byteLength / 2 * 2); - return byteLength / 2; - } else { - return paint.textToGlyphs(text, byteLength, glyphs); +size_t SkPDFFont::glyphsToPDFFontEncoding(const uint16_t* glyphIDs, + size_t numGlyphs, void* encodedValues, + size_t* encodedLength) { + if (numGlyphs * 2 > *encodedLength) + numGlyphs = *encodedLength / 2; + + // A font with multibyte glyphs will support all glyph IDs in a single font, + // shortcut if we can. + if (fMultiByteGlyphs) { + *encodedLength = numGlyphs * 2; + memcpy(encodedValues, glyphIDs, *encodedLength); + } else { + char* output = (char*) encodedValues; + for (size_t i = 0; i < numGlyphs; i++) { + if (glyphIDs[i] == 0) { + output[i] = 0; + continue; + } + if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) { + numGlyphs = i; + break; + } + output[i] = glyphIDs[i] - fFirstGlyphID + 1; } } - const char* data = (const char*)text; - const char* stop = data + byteLength; - const uint16_t* data16 = (const uint16_t*)data; - const uint16_t* stop16 = (const uint16_t*)stop; - uint16_t* gPtr = glyphs; - uint16_t* gEnd = glyphs + glyphsLength; - - // For a built-in font (no resources), we may have to undo glyph encoding - // before converting to the standard pdf encoding. - switch(paint.getTextEncoding()) { - case SkPaint::kUTF8_TextEncoding: - while (data < stop && gPtr < gEnd) - *gPtr++ = unicharToWinAnsiGlyphID(SkUTF8_NextUnichar(&data)); - SkASSERT(data >= stop); - break; - case SkPaint::kUTF16_TextEncoding: - while (data16 < stop16 && gPtr < gEnd) - *gPtr++ = unicharToWinAnsiGlyphID(SkUTF16_NextUnichar(&data16)); - SkASSERT(data16 >= stop16); - break; - case SkPaint::kGlyphID_TextEncoding: - while (data16 < stop16 && gPtr < gEnd) { - SkUnichar buf; - paint.glyphsToUnichars(data16++, 1, &buf); - *gPtr++ = unicharToWinAnsiGlyphID(buf); - } - SkASSERT(data16 >= stop16); - break; - default: - SkASSERT(!"Unknown text encoding"); - break; - } - return gPtr - glyphs; + return numGlyphs; } // static -SkPDFFont* SkPDFFont::getFontResouceByID(uint32_t fontID) { +SkPDFFont* SkPDFFont::getFontResource(uint32_t fontID, uint16_t glyphID) { SkAutoMutexAcquire lock(canonicalFontsMutex()); - int index = find(fontID); - if (index >= 0) { + int index; + if (find(fontID, glyphID, &index)) { canonicalFonts()[index].fFont->ref(); return canonicalFonts()[index].fFont; } - // TODO(vandebo) Lookup and create the font. For now, just use the built-in - // Helevtica. - SkPDFFont* font = new SkPDFFont(fontID, false); - font->populateBuiltinFont("Helvetica"); + SkRefPtr fontInfo; + SkPDFDict* fontDescriptor = NULL; + if (index >= 0) { + SkPDFFont* relatedFont = canonicalFonts()[index].fFont; + SkASSERT(relatedFont->fFontInfo.get()); + fontInfo = relatedFont->fFontInfo; + fontDescriptor = relatedFont->fDescriptor.get(); + } else { + fontInfo = SkFontHost::GetPDFTypefaceInfo(fontID); + fontInfo->unref(); // SkRefPtr and get info both took a reference. + } - FontRec newEntry(font, fontID); + SkPDFFont* font = new SkPDFFont(fontInfo.get(), fontID, glyphID, false, + fontDescriptor); + FontRec newEntry(font, fontID, font->fFirstGlyphID); + index = canonicalFonts().count(); canonicalFonts().push(newEntry); return font; // Return the reference new SkPDFFont() created. } @@ -141,59 +400,437 @@ SkMutex& SkPDFFont::canonicalFontsMutex() { } // static -int SkPDFFont::find(uint32_t fontID) { - FontRec search(NULL, fontID); - return canonicalFonts().find(search); +bool SkPDFFont::find(uint32_t fontID, uint16_t glyphID, int* index) { + // TODO(vandebo) optimize this, do only one search? + FontRec search(NULL, fontID, glyphID); + *index = canonicalFonts().find(search); + if (*index >= 0) + return true; + search.fGlyphID = 0; + *index = canonicalFonts().find(search); + return false; } -SkPDFFont::SkPDFFont(uint32_t fontID, bool multiByteGlyphs) - : fFontID(fontID), - fMultiByteGlyphs(multiByteGlyphs) { +SkPDFFont::SkPDFFont(class SkPDFTypefaceInfo* fontInfo, uint32_t fontID, + uint16_t glyphID, bool descendantFont, + SkPDFDict* fontDescriptor) + : SkPDFDict("Font"), + fFontID(fontID), +#ifdef SK_DEBUG + fDescendant(descendantFont), +#endif + fMultiByteGlyphs(false), + fFirstGlyphID(1), + fLastGlyphID(fontInfo->fLastGlyphID), + fFontInfo(fontInfo), + fDescriptor(fontDescriptor) { + + if (fontInfo->fMultiMaster) { + populateType3Font(); + } else { + switch (fontInfo->fType) { + case SkPDFTypefaceInfo::kType1CID_Font: + case SkPDFTypefaceInfo::kTrueType_Font: + if (descendantFont) + populateCIDFont(); + else + populateType0Font(); + break; + case SkPDFTypefaceInfo::kType1_Font: { + uint16_t firstGlyphID = glyphID - (glyphID - 1) % 255; + uint16_t lastGlyphID = firstGlyphID + 255 - 1; + if (lastGlyphID > fLastGlyphID) + lastGlyphID = fLastGlyphID; + if (populateType1Font(firstGlyphID, lastGlyphID)) + break; + // else, fall through. + } + case SkPDFTypefaceInfo::kOther_Font: + case SkPDFTypefaceInfo::kNotEmbeddable_Font: + populateType3Font(); + break; + case SkPDFTypefaceInfo::kCFF_Font: + SkASSERT(false); // Not supported yet. + } + } + + // Type1 fonts may hold on to the font info, otherwise we are done with it. + if (fontInfo->fType != SkPDFTypefaceInfo::kType1_Font) + fFontInfo = NULL; } -void SkPDFFont::populateBuiltinFont(const char fontName[]) { - SkASSERT(strcmp(fontName, "Time-Roman") == 0 || - strcmp(fontName, "Time-Bold") == 0 || - strcmp(fontName, "Time-Italic") == 0 || - strcmp(fontName, "Time-BoldItalic") == 0 || - strcmp(fontName, "Helvetica") == 0 || - strcmp(fontName, "Helvetica-Bold") == 0 || - strcmp(fontName, "Helvetica-Oblique") == 0 || - strcmp(fontName, "Helvetica-BoldOblique") == 0 || - strcmp(fontName, "Courier") == 0 || - strcmp(fontName, "Courier-Bold") == 0 || - strcmp(fontName, "Courier-Oblique") == 0 || - strcmp(fontName, "Courier-BoldOblique") == 0 || - strcmp(fontName, "Symbol") == 0 || - strcmp(fontName, "ZapfDingbats") == 0); +void SkPDFFont::populateType0Font() { + fMultiByteGlyphs = true; - SkRefPtr type = new SkPDFName("Font"); - type->unref(); // SkRefPtr and new both took a reference. - insert("Type", type.get()); + SkRefPtr subType = new SkPDFName("Type0"); + subType->unref(); // SkRefPtr and new both took a reference. + insert("Subtype", subType.get()); + + SkRefPtr baseFont = new SkPDFName(fFontInfo.get()->fFontName); + baseFont->unref(); // SkRefPtr and new both took a reference. + insert("BaseFont", baseFont.get()); + + SkRefPtr encoding = new SkPDFName("Identity-H"); + encoding->unref(); // SkRefPtr and new both took a reference. + insert("Encoding", encoding.get()); + + // TODO(vandebo) add a ToUnicode mapping. + + SkRefPtr cidFont = new SkPDFFont(fFontInfo.get(), + fFontID, 1, true, NULL); + fResources.push(cidFont.get()); // 2 refs: SkRefPtr, new. Pass one. + + SkRefPtr descendantFonts = new SkPDFArray(); + descendantFonts->unref(); // SkRefPtr and new took a reference. + SkRefPtr cidFontRef = new SkPDFObjRef(cidFont.get()); + cidFontRef->unref(); // SkRefPtr and new both took a reference. + descendantFonts->append(cidFontRef.get()); + insert("DescendantFonts", descendantFonts.get()); +} + +void SkPDFFont::populateCIDFont() { + fMultiByteGlyphs = true; + + SkRefPtr subType; + if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kType1CID_Font) + subType = new SkPDFName("CIDFontType0"); + else if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kTrueType_Font) + subType = new SkPDFName("CIDFontType2"); + else + SkASSERT(false); + subType->unref(); // SkRefPtr and new both took a reference. + insert("Subtype", subType.get()); + + SkRefPtr baseFont = new SkPDFName(fFontInfo.get()->fFontName); + baseFont->unref(); // SkRefPtr and new both took a reference. + insert("BaseFont", baseFont.get()); + + SkRefPtr sysInfo = new SkPDFDict; + sysInfo->unref(); // SkRefPtr and new both took a reference. + + SkRefPtr adobeString = new SkPDFString("Adobe"); + adobeString->unref(); // SkRefPtr and new both took a reference. + sysInfo->insert("Registry", adobeString.get()); + + SkRefPtr identityString = new SkPDFString("Identity"); + identityString->unref(); // SkRefPtr and new both took a reference. + sysInfo->insert("Ordering", identityString.get()); + + SkRefPtr supplement = new SkPDFInt(0); + supplement->unref(); // SkRefPtr and new both took a reference. + sysInfo->insert("Supplement", supplement.get()); + + insert("CIDSystemInfo", sysInfo.get()); + + addFontDescriptor(0); + + if (fFontInfo.get()->fGlyphWidths.get()) { + int defaultWidth = 0; + SkRefPtr widths = + composeAdvanceData(fFontInfo.get()->fGlyphWidths.get(), + &appendWidth, &defaultWidth); + widths->unref(); // SkRefPtr and compose both took a reference. + if (widths->size()) + insert("W", widths.get()); + if (defaultWidth != 0) { + SkRefPtr defaultWidthInt = + new SkPDFInt(defaultWidth); + // SkRefPtr and compose both took a reference. + defaultWidthInt->unref(); + insert("DW", defaultWidthInt.get()); + } + } + if (fFontInfo.get()->fVerticalMetrics.get()) { + struct SkPDFTypefaceInfo::VerticalMetric defaultAdvance; + defaultAdvance.fVerticalAdvance = 0; + defaultAdvance.fOriginXDisp = 0; + defaultAdvance.fOriginYDisp = 0; + SkRefPtr advances = + composeAdvanceData(fFontInfo.get()->fVerticalMetrics.get(), + &appendVerticalAdvance, &defaultAdvance); + advances->unref(); // SkRefPtr and compose both took a ref. + if (advances->size()) + insert("W2", advances.get()); + if (defaultAdvance.fVerticalAdvance || + defaultAdvance.fOriginXDisp || + defaultAdvance.fOriginYDisp) { + SkRefPtr defaultAdvanceArray = new SkPDFArray; + // SkRefPtr and compose both took a reference. + defaultAdvanceArray->unref(); + appendVerticalAdvance(defaultAdvance, + defaultAdvanceArray.get()); + insert("DW2", defaultAdvanceArray.get()); + } + } +} + +bool SkPDFFont::populateType1Font(uint16_t firstGlyphID, uint16_t lastGlyphID) { + SkASSERT(!fFontInfo.get()->fVerticalMetrics.get()); + SkASSERT(fFontInfo.get()->fGlyphWidths.get()); + + int defaultWidth = 0; + const SkPDFTypefaceInfo::WidthRange* widthRangeEntry = NULL; + const SkPDFTypefaceInfo::WidthRange* widthEntry; + for (widthEntry = fFontInfo.get()->fGlyphWidths.get(); + widthEntry != NULL; + widthEntry = widthEntry->fNext.get()) { + switch (widthEntry->fType) { + case SkPDFTypefaceInfo::WidthRange::kDefault: + defaultWidth = widthEntry->fAdvance[0]; + break; + case SkPDFTypefaceInfo::WidthRange::kRun: + SkASSERT(false); + break; + case SkPDFTypefaceInfo::WidthRange::kRange: + SkASSERT(widthRangeEntry == NULL); + widthRangeEntry = widthEntry; + break; + } + } + + if (!addFontDescriptor(defaultWidth)) + return false; + + fFirstGlyphID = firstGlyphID; + fLastGlyphID = lastGlyphID; SkRefPtr subType = new SkPDFName("Type1"); subType->unref(); // SkRefPtr and new both took a reference. insert("Subtype", subType.get()); - SkRefPtr baseFont = new SkPDFName(fontName); + SkRefPtr baseFont = new SkPDFName(fFontInfo.get()->fFontName); baseFont->unref(); // SkRefPtr and new both took a reference. insert("BaseFont", baseFont.get()); - SkRefPtr encoding = new SkPDFName("WinAnsiEncoding"); + SkRefPtr widthArray = new SkPDFArray(); + widthArray->unref(); // SkRefPtr and new both took a ref. + int firstChar = 0; + if (widthRangeEntry) { + int startIndex = firstGlyphID - widthRangeEntry->fStartId; + int endIndex = startIndex + lastGlyphID - firstGlyphID + 1; + if (startIndex < 0) + startIndex = 0; + if (endIndex > widthRangeEntry->fAdvance.count()) + endIndex = widthRangeEntry->fAdvance.count(); + if (widthRangeEntry->fStartId == 0) { + appendWidth(widthRangeEntry->fAdvance[0], widthArray.get()); + } else { + firstChar = startIndex + widthRangeEntry->fStartId; + } + for (int i = startIndex; i < endIndex; i++) + appendWidth(widthRangeEntry->fAdvance[i], widthArray.get()); + } else { + appendWidth(defaultWidth, widthArray.get()); + } + insert("Widths", widthArray.get()); + + SkRefPtr firstCharInt = new SkPDFInt(firstChar); + firstCharInt->unref(); // SkRefPtr and new both took a reference. + insert("FirstChar", firstCharInt.get()); + + SkRefPtr lastChar = + new SkPDFInt(firstChar + widthArray->size() - 1); + lastChar->unref(); // SkRefPtr and new both took a reference. + insert("LastChar", lastChar.get()); + + SkRefPtr encoding = new SkPDFDict("Encoding"); encoding->unref(); // SkRefPtr and new both took a reference. insert("Encoding", encoding.get()); + + SkRefPtr encDiffs = new SkPDFArray; + encDiffs->unref(); // SkRefPtr and new both took a reference. + encoding->insert("Differences", encDiffs.get()); + + encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2); + SkRefPtr startID = new SkPDFInt(1); + startID->unref(); // SkRefPtr and new both took a reference. + encDiffs->append(startID.get()); + for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) { + SkRefPtr glyphName = + new SkPDFName(fFontInfo.get()->fGlyphNames->get()[gID]); + glyphName->unref(); // SkRefPtr and new both took a reference. + encDiffs->append(glyphName.get()); + } + + if (fFontInfo.get()->fLastGlyphID <= 255) + fFontInfo = NULL; + return true; } -void SkPDFFont::populateFont(const char subType[], const char fontName[], - int firstChar, int lastChar, int widths[], - SkPDFObject* fontDescriptor) { +void SkPDFFont::populateType3Font() { + // TODO(vandebo) + SkASSERT(false); } +bool SkPDFFont::addFontDescriptor(int defaultWidth) { + if (fDescriptor.get() != NULL) { + fResources.push(fDescriptor.get()); + fDescriptor->ref(); + SkRefPtr descRef = new SkPDFObjRef(fDescriptor.get()); + descRef->unref(); // SkRefPtr and new both took a reference. + insert("FontDescriptor", descRef.get()); + return true; + } + + fDescriptor = new SkPDFDict("FontDescriptor"); + fDescriptor->unref(); // SkRefPtr and new both took a ref. + + switch (fFontInfo.get()->fType) { + case SkPDFTypefaceInfo::kType1_Font: { + size_t header, data, trailer; + SkRefPtr rawFontData = + SkFontHost::OpenStream(fFontID); + rawFontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkStream* fontData = handleType1Stream(rawFontData.get(), &header, + &data, &trailer); + if (fontData == NULL) + return false; + SkRefPtr fontStream = new SkPDFStream(fontData); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + + SkRefPtr headerLen = new SkPDFInt(header); + headerLen->unref(); // SkRefPtr and new both took a reference. + fontStream->insert("Length1", headerLen.get()); + SkRefPtr dataLen = new SkPDFInt(data); + dataLen->unref(); // SkRefPtr and new both took a reference. + fontStream->insert("Length2", dataLen.get()); + SkRefPtr trailerLen = new SkPDFInt(trailer); + trailerLen->unref(); // SkRefPtr and new both took a reference. + fontStream->insert("Length3", trailerLen.get()); + + SkRefPtr streamRef = new SkPDFObjRef(fontStream.get()); + streamRef->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("FontFile", streamRef.get()); + break; + } + case SkPDFTypefaceInfo::kTrueType_Font: { + SkRefPtr fontData = SkFontHost::OpenStream(fFontID); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr fontStream = new SkPDFStream(fontData.get()); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + + SkRefPtr length = new SkPDFInt(fontData->getLength()); + length->unref(); // SkRefPtr and new both took a reference. + fontStream->insert("Length1", length.get()); + + SkRefPtr streamRef = new SkPDFObjRef(fontStream.get()); + streamRef->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("FontFile2", streamRef.get()); + break; + } + case SkPDFTypefaceInfo::kCFF_Font: + case SkPDFTypefaceInfo::kType1CID_Font: { + SkRefPtr fontData = SkFontHost::OpenStream(fFontID); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr fontStream = new SkPDFStream(fontData.get()); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + + SkRefPtr subtype; + if (fFontInfo.get()->fType == SkPDFTypefaceInfo::kCFF_Font) + subtype = new SkPDFName("Type1C"); + else + subtype = new SkPDFName("CIDFontType0c"); + subtype->unref(); // SkRefPtr and new both took a reference. + fontStream->insert("Subtype", subtype.get()); + + SkRefPtr streamRef = new SkPDFObjRef(fontStream.get()); + streamRef->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("FontFile3", streamRef.get()); + break; + } + default: + SkASSERT(false); + } + + fResources.push(fDescriptor.get()); + fDescriptor->ref(); + SkRefPtr descRef = new SkPDFObjRef(fDescriptor.get()); + descRef->unref(); // SkRefPtr and new both took a reference. + insert("FontDescriptor", descRef.get()); + + SkRefPtr fontName = + new SkPDFName(fFontInfo.get()->fFontName); + fontName->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("FontName", fontName.get()); + + SkRefPtr flags = new SkPDFInt(fFontInfo.get()->fStyle); + flags->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("Flags", flags.get()); + + SkIRect glyphBBox = fFontInfo.get()->fBBox; + SkRefPtr bbox = new SkPDFArray; + bbox->unref(); // SkRefPtr and new both took a reference. + bbox->reserve(4); + SkRefPtr bboxXMin = new SkPDFInt(glyphBBox.fLeft); + bboxXMin->unref(); // SkRefPtr and new both took a reference. + bbox->append(bboxXMin.get()); + SkRefPtr bboxYMin = new SkPDFInt(glyphBBox.fBottom); + bboxYMin->unref(); // SkRefPtr and new both took a reference. + bbox->append(bboxYMin.get()); + SkRefPtr bboxXMax = new SkPDFInt(glyphBBox.fRight); + bboxXMax->unref(); // SkRefPtr and new both took a reference. + bbox->append(bboxXMax.get()); + SkRefPtr bboxYMax = new SkPDFInt(glyphBBox.fTop); + bboxYMax->unref(); // SkRefPtr and new both took a reference. + bbox->append(bboxYMax.get()); + fDescriptor->insert("FontBBox", bbox.get()); + + SkRefPtr italicAngle = + new SkPDFInt(fFontInfo.get()->fItalicAngle); + italicAngle->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("ItalicAngle", italicAngle.get()); + + SkRefPtr ascent = new SkPDFScalar(fFontInfo.get()->fAscent); + ascent->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("Ascent", ascent.get()); + + SkRefPtr descent = new SkPDFScalar(fFontInfo.get()->fDescent); + descent->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("Descent", descent.get()); + + SkRefPtr capHeight = + new SkPDFScalar(fFontInfo.get()->fCapHeight); + capHeight->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("CapHeight", capHeight.get()); + + SkRefPtr stemV = new SkPDFScalar(fFontInfo.get()->fStemV); + stemV->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("StemV", stemV.get()); + + if (defaultWidth > 0) { + SkRefPtr defaultWidthInt = new SkPDFInt(defaultWidth); + defaultWidthInt->unref(); // SkRefPtr and new both took a reference. + fDescriptor->insert("MissingWidth", defaultWidthInt.get()); + } + return true; +} + + bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const { - return fFontID == b.fFontID; + if (fFontID != b.fFontID) + return false; + if (fFont != NULL && b.fFont != NULL) { + return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID && + fFont->fLastGlyphID == b.fFont->fLastGlyphID; + } + if (fGlyphID == 0 || b.fGlyphID == 0) + return true; + + if (fFont != NULL) { + return fFont->fFirstGlyphID <= b.fGlyphID && + b.fGlyphID <= fFont->fLastGlyphID; + } else if (b.fFont != NULL) { + return b.fFont->fFirstGlyphID <= fGlyphID && + fGlyphID <= b.fFont->fLastGlyphID; + } + return fGlyphID == b.fGlyphID; } -SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID) +SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID) : fFont(font), - fFontID(fontID) { + fFontID(fontID), + fGlyphID(glyphID) { } diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp index 1fb62fa199..286468bb62 100644 --- a/src/pdf/SkPDFGraphicState.cpp +++ b/src/pdf/SkPDFGraphicState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,7 +114,7 @@ void SkPDFGraphicState::populateDict() { SkRefPtr strokeMiterLimit = new SkPDFScalar( fPaint.getStrokeMiter()); strokeMiterLimit->unref(); // SkRefPtr and new both took a reference. - insert("ML", strokeWidth.get()); + insert("ML", strokeMiterLimit.get()); // Turn on automatic stroke adjustment. SkRefPtr trueVal = new SkPDFBool(true); diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index 9649f0baec..506b86d37f 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,7 +97,7 @@ void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog, } SkPDFString::SkPDFString(const char value[]) - : fValue(formatString(value, sizeof(value))) { + : fValue(formatString(value, strlen(value))) { } SkPDFString::SkPDFString(const SkString& value) @@ -156,7 +156,7 @@ SkString SkPDFString::doFormatString(const void* input, size_t len, for (size_t i = 0; i < len; i++) { SkASSERT(!wideInput || !(win[i] & ~0xFF)); char val = wideInput ? win[i] : cin[i]; - if (val & 0x80 || val < ' ') { + if (val > '~' || val < ' ') { sevenBitClean = false; break; } @@ -177,7 +177,7 @@ SkString SkPDFString::doFormatString(const void* input, size_t len, result.append("<"); for (size_t i = 0; i < len; i++) { SkASSERT(!wideInput || !(win[i] & ~0xFF)); - char val = wideInput ? win[i] : cin[i]; + unsigned char val = wideInput ? win[i] : cin[i]; result.appendHex(val, 2); } result.append(">"); diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp index 308adf8070..79e61c0e63 100644 --- a/src/ports/SkFontHost_FONTPATH.cpp +++ b/src/ports/SkFontHost_FONTPATH.cpp @@ -269,6 +269,12 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, return SkNEW_ARGS(FontFaceRec_Typeface, (face)); } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + sk_throw(); // not implemented + return NULL; +} + SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { sk_throw(); // not implemented return NULL; diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 9af21bc981..c05e982215 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -15,27 +15,30 @@ ** limitations under the License. */ -#include "SkColorPriv.h" -#include "SkScalerContext.h" #include "SkBitmap.h" #include "SkCanvas.h" +#include "SkColorPriv.h" #include "SkDescriptor.h" #include "SkFDot6.h" #include "SkFontHost.h" #include "SkMask.h" +#include "SkPDFTypefaceInfo.h" +#include "SkScalerContext.h" #include "SkStream.h" #include "SkString.h" -#include "SkThread.h" #include "SkTemplates.h" +#include "SkThread.h" #include #include FT_FREETYPE_H #include FT_OUTLINE_H #include FT_SIZES_H #include FT_TRUETYPE_TABLES_H +#include FT_TYPE1_TABLES_H #include FT_BITMAP_H // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. #include FT_SYNTHESIS_H +#include FT_XFREE86_H #if defined(SK_SUPPORT_LCDTEXT) #include FT_LCD_FILTER_H @@ -304,6 +307,285 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { rec->setHinting(h); } +static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) { + const FT_UInt glyph_id = FT_Get_Char_Index(face, letter); + if (!glyph_id) + return false; + FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE); + FT_Outline_Get_CBox(&face->glyph->outline, bbox); + return true; +} + +int getWidthAdvance(FT_Face face, int gId, int scaleDivisor) { + FT_Fixed unscaledAdvance = 0; + SkAssertResult(FT_Get_Advance(face, gId, FT_LOAD_NO_SCALE, + &unscaledAdvance) == 0); + return unscaledAdvance * 1000 / scaleDivisor; +} + +template +void resetRange(SkPDFTypefaceInfo::AdvanceMetric* range, int startId) { + range->fStartId = startId; + range->fAdvance.setCount(0); +} + +template +SkPDFTypefaceInfo::AdvanceMetric* appendRange( + SkTScopedPtr >* nextSlot, + int startId) { + nextSlot->reset(new SkPDFTypefaceInfo::AdvanceMetric); + resetRange(nextSlot->get(), startId); + return nextSlot->get(); +} + +template +void finishRange( + SkPDFTypefaceInfo::AdvanceMetric* range, + int endId, + typename SkPDFTypefaceInfo::AdvanceMetric::MetricType type) { + range->fEndId = endId; + range->fType = type; + int newLength; + if (type == SkPDFTypefaceInfo::AdvanceMetric::kRange) + newLength = endId - range->fStartId + 1; + else + newLength = 1; + SkASSERT(range->fAdvance.count() >= newLength); + range->fAdvance.setCount(newLength); +} + +template +SkPDFTypefaceInfo::AdvanceMetric* getAdvanceData( + FT_Face face, + int scaleDivisor, + Data (*getAdvance)(FT_Face face, int gId, int scaleDivisor)) { + // Assuming that an ASCII representation of a width or a glyph id is, + // on average, 3 characters long gives the following cut offs for + // using different range types: + // When currently in a range + // - Removing 4 0's is a win + // - Removing 5 repeats is a win + // When not currently in a range + // - Removing 1 0 is a win + // - Removing 3 repeats is a win + + SkTScopedPtr > result; + SkPDFTypefaceInfo::AdvanceMetric* curRange; + curRange = appendRange(&result, 0); + int lastAdvance = INT_MIN; + int repeats = 0; + for (FT_UInt gId = 0; gId < face->num_glyphs; gId++) { + int advance = getAdvance(face, gId, scaleDivisor); + if (advance == lastAdvance) { + repeats++; + } else if (curRange->fAdvance.count() == repeats + 1) { + if (lastAdvance == 0 && repeats >= 0) { + resetRange(curRange, gId); + } else if (repeats >= 2) { + finishRange(curRange, gId - 1, + SkPDFTypefaceInfo::WidthRange::kRun); + curRange = appendRange(&curRange->fNext, gId); + } + repeats = 0; + } else { + if (lastAdvance == 0 && repeats >= 3) { + finishRange(curRange, gId - repeats - 2, + SkPDFTypefaceInfo::WidthRange::kRange); + curRange = appendRange(&curRange->fNext, gId); + } else if (repeats >= 4) { + finishRange(curRange, gId - repeats - 2, + SkPDFTypefaceInfo::WidthRange::kRange); + curRange = appendRange(&curRange->fNext, gId - repeats - 1); + curRange->fAdvance.append(1, &lastAdvance); + finishRange(curRange, gId - 1, + SkPDFTypefaceInfo::WidthRange::kRun); + curRange = appendRange(&curRange->fNext, gId); + } + repeats = 0; + } + curRange->fAdvance.append(1, &advance); + lastAdvance = advance; + } + finishRange(curRange, face->num_glyphs - 1, + SkPDFTypefaceInfo::WidthRange::kRange); + return result.release(); +} + +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkAutoMutexAcquire ac(gFTMutex); + FT_Library libInit = NULL; + if (gFTCount == 0) { + if (!InitFreetype()) + sk_throw(); + libInit = gFTLibrary; + } + SkAutoTCallIProc ftLib(libInit); + SkFaceRec* rec = ref_ft_face(fontID); + if (NULL == rec) + return NULL; + FT_Face face = rec->fFace; + + SkPDFTypefaceInfo* info = new SkPDFTypefaceInfo; + info->fMultiMaster = false; + bool cid = false; + int scaleDivisor = 1000; + const char* fontType = FT_Get_X11_Font_Format(face); + if (FT_Get_FSType_Flags(face) & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | + FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) { + info->fType = SkPDFTypefaceInfo::kNotEmbeddable_Font; + } else if (FT_HAS_MULTIPLE_MASTERS(face)) { + // PDF requires that embedded MM fonts be reduced to a simple font. + info->fType = SkPDFTypefaceInfo::kOther_Font; + } else if (strcmp(fontType, "Type 1") == 0) { + info->fType = SkPDFTypefaceInfo::kType1_Font; + } else if (strcmp(fontType, "CID Type 1") == 0) { + info->fType = SkPDFTypefaceInfo::kType1CID_Font; + cid = true; + } else if (strcmp(fontType, "CFF") == 0) { + info->fType = SkPDFTypefaceInfo::kCFF_Font; + } else if (strcmp(fontType, "TrueType") == 0) { + info->fType = SkPDFTypefaceInfo::kTrueType_Font; + cid = true; + TT_Header* ttHeader; + if ((ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, + ft_sfnt_head)) != NULL) { + scaleDivisor = ttHeader->Units_Per_EM; + } + } + info->fFontName.set(FT_Get_Postscript_Name(face)); + SkASSERT(FT_IS_CID_KEYED(face) == + (info->fType == SkPDFTypefaceInfo::kType1CID_Font)); + + if (info->fType == SkPDFTypefaceInfo::kOther_Font || + info->fType == SkPDFTypefaceInfo::kNotEmbeddable_Font || + !FT_IS_SCALABLE(face)) { + unref_ft_face(face); + return info; + } + + info->fLastGlyphID = face->num_glyphs - 1; + + SkASSERT(!FT_HAS_VERTICAL(face)); + + if (FT_IS_FIXED_WIDTH(face)) { + appendRange(&info->fGlyphWidths, 0); + int advance = face->max_advance_width * 1000 / scaleDivisor; + info->fGlyphWidths->fAdvance.append(1, &advance); + finishRange(info->fGlyphWidths.get(), 0, + SkPDFTypefaceInfo::WidthRange::kDefault); + } else if(!cid) { + appendRange(&info->fGlyphWidths, 0); + // So as to not blow out the stack, get advances in batches. + for (int gIDStart = 0; gIDStart < face->num_glyphs; gIDStart += 128) { + FT_Fixed advances[128]; + int advanceCount = 128; + if (gIDStart + advanceCount > face->num_glyphs) + advanceCount = face->num_glyphs - gIDStart + 1; + FT_Get_Advances(face, gIDStart, advanceCount, FT_LOAD_NO_SCALE, + advances); + for (int i = 0; i < advanceCount; i++) { + int advance = advances[gIDStart + i]; + info->fGlyphWidths->fAdvance.append(1, &advance); + } + } + finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1, + SkPDFTypefaceInfo::WidthRange::kRange); + } else { + // For CID keyed fonts, an identity pdf-cmap is used, so we iterate and + // report on glyph ids, not character ids (tt-cmap indices). + info->fGlyphWidths.reset(getAdvanceData(face, scaleDivisor, + &getWidthAdvance)); + } + + if (info->fType == SkPDFTypefaceInfo::kType1_Font) { + // Postscript fonts may contain more than 255 glyphs, so we end up + // using multiple font descriptions with a glyph ordering. Record + // the name of each glyph. + info->fGlyphNames.reset(new SkAutoTArray(face->num_glyphs)); + for (FT_UInt gID = 0; gID < face->num_glyphs; gID++) { + char glyphName[128]; // Postscript limit for names is 127 bytes. + FT_Get_Glyph_Name(face, gID, glyphName, 128); + info->fGlyphNames->get()[gID].set(glyphName); + } + } + + info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin * 1000 / scaleDivisor, + face->bbox.yMax * 1000 / scaleDivisor, + face->bbox.xMax * 1000 / scaleDivisor, + face->bbox.yMin * 1000 / scaleDivisor); + info->fStyle = 0; + if (FT_IS_FIXED_WIDTH(face)) + info->fStyle |= SkPDFTypefaceInfo::kFixedPitch_Style; + if (face->style_flags & FT_STYLE_FLAG_ITALIC) + info->fStyle |= SkPDFTypefaceInfo::kItalic_Style; + // We should set either Symbolic or Nonsymbolic; Nonsymbolic if the font's + // character set is a subset of 'Adobe standard Latin.' + info->fStyle |= SkPDFTypefaceInfo::kSymbolic_Style; + + TT_PCLT* pclt_info; + TT_OS2* os2_table; + if ((pclt_info = (TT_PCLT*)FT_Get_Sfnt_Table(face, ft_sfnt_pclt)) != NULL) { + info->fCapHeight = pclt_info->CapHeight; + uint8_t serif_style = pclt_info->SerifStyle & 0x3F; + if (serif_style >= 2 && serif_style <= 6) + info->fStyle |= SkPDFTypefaceInfo::kSerif_Style; + else if (serif_style >= 9 && serif_style <= 12) + info->fStyle |= SkPDFTypefaceInfo::kScript_Style; + } else if ((os2_table = + (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) { + info->fCapHeight = os2_table->sCapHeight; + } else { + // Figure out a good guess for CapHeight: average the height of M and X. + FT_BBox m_bbox, x_bbox; + bool got_m, got_x; + got_m = GetLetterCBox(face, 'M', &m_bbox); + got_x = GetLetterCBox(face, 'X', &x_bbox); + if (got_m && got_x) { + info->fCapHeight = (m_bbox.yMax - m_bbox.yMin + x_bbox.yMax - + x_bbox.yMin) / 2; + } else if (got_m && !got_x) { + info->fCapHeight = m_bbox.yMax - m_bbox.yMin; + } else if (!got_m && got_x) { + info->fCapHeight = x_bbox.yMax - x_bbox.yMin; + } + } + info->fCapHeight = info->fCapHeight * 1000 / scaleDivisor; + + // Figure out a good guess for StemV - Min width of i, I, !, 1. + // This probably isn't very good with an italic font. + int min_width = INT_MAX; + char stem_chars[] = {'i', 'I', '!', '1'}; + for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { + FT_BBox bbox; + if (GetLetterCBox(face, stem_chars[i], &bbox)) { + int width = bbox.xMax - bbox.xMin; + if (width > 0 && width < min_width) { + min_width = width; + info->fStemV = min_width * 1000 / scaleDivisor; + } + } + } + + PS_FontInfoRec ps_info; + TT_Postscript* tt_info; + if (FT_Get_PS_Font_Info(face, &ps_info) == 0) { + info->fItalicAngle = ps_info.italic_angle; + } else if ((tt_info = + (TT_Postscript*)FT_Get_Sfnt_Table(face, + ft_sfnt_post)) != NULL) { + info->fItalicAngle = SkFixedToScalar(tt_info->italicAngle); + } else { + info->fItalicAngle = 0; + } + + info->fAscent = face->ascender * 1000 / scaleDivisor; + info->fDescent = face->descender * 1000 / scaleDivisor; + + unref_ft_face(face); + return info; +} + SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc) { SkAutoMutexAcquire ac(gFTMutex); diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 28b9ff2f6a..98e8df59e8 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -584,6 +584,12 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, return tf; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + bool SkFontHost::ValidFontID(uint32_t fontID) { SkAutoMutexAcquire ac(gFamilyMutex); diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp index d1da8d1589..f11c985574 100644 --- a/src/ports/SkFontHost_fontconfig.cpp +++ b/src/ports/SkFontHost_fontconfig.cpp @@ -300,6 +300,12 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, return typeface; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + // static SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { diff --git a/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp index 9c01dfe7f6..361d5ce6bb 100644 --- a/src/ports/SkFontHost_mac_atsui.cpp +++ b/src/ports/SkFontHost_mac_atsui.cpp @@ -479,6 +479,12 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { return NULL; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { return new SkScalerContext_Mac(desc); } diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp index 2fc5c1dbbf..f6839bc4bf 100644 --- a/src/ports/SkFontHost_mac_coretext.cpp +++ b/src/ports/SkFontHost_mac_coretext.cpp @@ -603,6 +603,12 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal); } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + /////////////////////////////////////////////////////////////////////////// bool SkFontHost::ValidFontID(SkFontID uniqueID) diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp index ae56ae4e14..d2d0a9f25e 100644 --- a/src/ports/SkFontHost_none.cpp +++ b/src/ports/SkFontHost_none.cpp @@ -33,6 +33,12 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) { return NULL; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { } diff --git a/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp index 9e126bccf8..ccec75b9f9 100644 --- a/src/ports/SkFontHost_simple.cpp +++ b/src/ports/SkFontHost_simple.cpp @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "SkFontHost.h" #include "SkDescriptor.h" #include "SkMMapStream.h" @@ -566,6 +582,12 @@ SkStream* SkFontHost::OpenStream(uint32_t fontID) { return stream; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) { SkAutoMutexAcquire ac(gFamilyMutex); diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index a1114d90d5..4f3f198bd3 100644 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -463,6 +463,12 @@ SkTypeface* SkFontHost::Deserialize(SkStream* stream) { return NULL; } +// static +SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) { + SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented"); + return NULL; +} + SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { //Should not be used on Windows, keep linker happy diff --git a/tests/FlateTest.cpp b/tests/FlateTest.cpp index b9befe0c32..f8e0921a17 100644 --- a/tests/FlateTest.cpp +++ b/tests/FlateTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ // A memory stream that reports zero size with the standard call, like // an unseekable file stream would. -class SkSimulatedFileStream : public SkMemoryStream { +class SkZeroSizeMemStream : public SkMemoryStream { public: virtual size_t read(void* buffer, size_t size) { if (buffer == NULL && size == 0) @@ -55,7 +55,7 @@ static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, // Check that the input data wasn't changed. size_t inputSize = testStream->getLength(); if (inputSize == 0) - inputSize = testStream->read(NULL, SkSimulatedFileStream::kGetSizeKey); + inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); REPORTER_ASSERT(reporter, testData.getLength() == inputSize); REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(), testStream->getMemoryBase(), @@ -75,7 +75,7 @@ static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream, // Check that the input data wasn't changed. inputSize = testStream->getLength(); if (inputSize == 0) - inputSize = testStream->read(NULL, SkSimulatedFileStream::kGetSizeKey); + inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey); REPORTER_ASSERT(reporter, compressed.getOffset() == inputSize); REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(), compressed.getStream(), @@ -97,7 +97,7 @@ static void TestFlateCompression(skiatest::Reporter* reporter) { TestFlate(reporter, &memStream, 512); TestFlate(reporter, &memStream, 10240); - SkSimulatedFileStream fileStream; + SkZeroSizeMemStream fileStream; TestFlate(reporter, &fileStream, 512); TestFlate(reporter, &fileStream, 10240); #endif