aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-01-25 21:01:34 +0000
committerGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-01-25 21:01:34 +0000
commit2a22e10ab2946c5590cd2a258427ce3ccfca9bfa (patch)
tree1d96a3c68ca4bbe51935d5d2f56e46ccfd63f969
parentc921843d85b4e79857103cdb614f76f123efa7f5 (diff)
Add Truetype and Type 1 font embedding support
Sorry this is such a large CL. It was very exploratory for me to make this work. - Add an interface to SkFontHost to retrieve font information and provide NULL implementations on all platforms except Linux. - Segment large Type 1 fonts into fonts with shared resources with 255 glyphs each. - Convert the various Type 1 formats to the form PDF wants. - Update font as we draw text instead of as part of the graphical state. - Remove built-in font support, we can't really use it. Other changes I can pull out to a separate CL if you like. - Add SkTScopedPtr class. - Fix double free of resources. - Fix bug in resource unique-ifying code. - Don't print anything for any empty clip path. - Fix copy paste error - MiterLimit. - Fix sign extension bug in SkPDFString - Fix FlateTest rename that was missed on a previous commit. Review URL: http://codereview.appspot.com/4082042 git-svn-id: http://skia.googlecode.com/svn/trunk@728 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkFontHost.h5
-rw-r--r--include/core/SkTScopedPtr.h83
-rw-r--r--include/core/SkTemplates.h1
-rw-r--r--include/core/SkTypeface.h6
-rw-r--r--include/pdf/SkPDFDevice.h10
-rw-r--r--include/pdf/SkPDFFont.h87
-rw-r--r--include/pdf/SkPDFTypefaceInfo.h122
-rw-r--r--src/core/SkTypeface.cpp20
-rw-r--r--src/pdf/SkPDFDevice.cpp155
-rw-r--r--src/pdf/SkPDFDocument.cpp24
-rw-r--r--src/pdf/SkPDFFont.cpp843
-rw-r--r--src/pdf/SkPDFGraphicState.cpp4
-rw-r--r--src/pdf/SkPDFTypes.cpp8
-rw-r--r--src/ports/SkFontHost_FONTPATH.cpp6
-rw-r--r--src/ports/SkFontHost_FreeType.cpp288
-rw-r--r--src/ports/SkFontHost_android.cpp6
-rw-r--r--src/ports/SkFontHost_fontconfig.cpp6
-rw-r--r--src/ports/SkFontHost_mac_atsui.cpp6
-rw-r--r--src/ports/SkFontHost_mac_coretext.cpp6
-rw-r--r--src/ports/SkFontHost_none.cpp6
-rw-r--r--src/ports/SkFontHost_simple.cpp22
-rw-r--r--src/ports/SkFontHost_win.cpp6
-rw-r--r--tests/FlateTest.cpp10
23 files changed, 1530 insertions, 200 deletions
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<T> is like a T*, except that the destructor of SkTScopedPtr<T>
+ automatically deletes the pointer it holds (if any). That is, SkTScopedPtr<T>
+ owns the T object that it points to. Like a T*, a SkTScopedPtr<T> may hold
+ either NULL or a pointer to a T object. Also like T*, SkTScopedPtr<T> is
+ thread-compatible, and once you dereference it, you get the threadsafety
+ guarantees of T.
+
+ The size of a SkTScopedPtr is small: sizeof(SkTScopedPtr<T>) == sizeof(T*)
+*/
+template <typename T> 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 <class T2> bool operator==(SkTScopedPtr<T2> const& o2) const;
+ template <class T2> bool operator!=(SkTScopedPtr<T2> 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 <typename T> 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<SkPDFFont> fFont;
- SkRefPtr<SkPDFGraphicState> 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;
-
- /** 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.
+ size_t glyphsToPDFFontEncoding(const uint16_t* glyphIDs, size_t numGlyphs,
+ void* encodedValues, size_t* encodedLength);
+
+ /** 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<SkPDFTypefaceInfo> fFontInfo;
SkTDArray<SkPDFObject*> fResources;
+ SkRefPtr<SkPDFDict> 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<FontRec>& 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 <typename Data>
+ 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<Data> fAdvance;
+ SkTScopedPtr<AdvanceMetric<Data> > 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<int> WidthRange;
+ typedef AdvanceMetric<VerticalMetric> VerticalAdvanceRange;
+
+ // This is indexed by glyph id.
+ SkTScopedPtr<WidthRange> fGlyphWidths;
+ // Only used for Vertical CID fonts.
+ SkTScopedPtr<VerticalAdvanceRange> fVerticalMetrics;
+
+ // The names of each glyph, only populated for postscript fonts.
+ SkTScopedPtr<SkAutoTArray<SkString> > 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);
-
- 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 ");
+ 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 ");
+ }
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<SkPDFFont> 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<SkPDFFont> 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<SkPDFObjRef> pageTreeRootRef = new SkPDFObjRef(pageTreeRoot);
pageTreeRootRef->unref(); // SkRefPtr and new both took a reference.
fDocCatalog->insert("Pages", pageTreeRootRef.get());
+
+ /* TODO(vandebo) output intent
+ SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
+ outputIntent->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFName> intentSubtype = new SkPDFName("GTS_PDFA1");
+ intentSubtype->unref(); // SkRefPtr and new both took a reference.
+ outputIntent->insert("S", intentSubtype.get());
+ SkRefPtr<SkPDFString> intentIdentifier = new SkPDFString("sRGB");
+ intentIdentifier->unref(); // SkRefPtr and new both took a reference.
+ outputIntent->insert("OutputConditionIdentifier",
+ intentIdentifier.get());
+ SkRefPtr<SkPDFArray> 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 <ctype.h>
+
+#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<SkMemoryStream> 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<SkPDFInt> 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 <typename Data>
+SkPDFArray* composeAdvanceData(
+ SkPDFTypefaceInfo::AdvanceMetric<Data>* 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<SkPDFArray> 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<SkPDFInt> 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<SkPDFInt> rangeStart =
+ new SkPDFInt(advanceInfo->fStartId);
+ rangeStart->unref(); // SkRefPtr and new both took a reference.
+ result->append(rangeStart.get());
+
+ SkRefPtr<SkPDFInt> 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<SkPDFObject*>* 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;
- 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);
+ // 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;
}
- SkASSERT(data16 >= stop16);
- break;
- default:
- SkASSERT(!"Unknown text encoding");
- break;
+ if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) {
+ numGlyphs = i;
+ break;
+ }
+ output[i] = glyphIDs[i] - fFirstGlyphID + 1;
+ }
}
- 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<SkPDFTypefaceInfo> 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);
-}
-
-SkPDFFont::SkPDFFont(uint32_t fontID, bool multiByteGlyphs)
- : fFontID(fontID),
- fMultiByteGlyphs(multiByteGlyphs) {
-}
-
-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);
-
- SkRefPtr<SkPDFName> type = new SkPDFName("Font");
- type->unref(); // SkRefPtr and new both took a reference.
- insert("Type", type.get());
+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(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::populateType0Font() {
+ fMultiByteGlyphs = true;
+
+ SkRefPtr<SkPDFName> subType = new SkPDFName("Type0");
+ subType->unref(); // SkRefPtr and new both took a reference.
+ insert("Subtype", subType.get());
+
+ SkRefPtr<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
+ baseFont->unref(); // SkRefPtr and new both took a reference.
+ insert("BaseFont", baseFont.get());
+
+ SkRefPtr<SkPDFName> 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<SkPDFFont> cidFont = new SkPDFFont(fFontInfo.get(),
+ fFontID, 1, true, NULL);
+ fResources.push(cidFont.get()); // 2 refs: SkRefPtr, new. Pass one.
+
+ SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
+ descendantFonts->unref(); // SkRefPtr and new took a reference.
+ SkRefPtr<SkPDFObjRef> 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<SkPDFName> 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<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
+ baseFont->unref(); // SkRefPtr and new both took a reference.
+ insert("BaseFont", baseFont.get());
+
+ SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
+ sysInfo->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFString> adobeString = new SkPDFString("Adobe");
+ adobeString->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Registry", adobeString.get());
+
+ SkRefPtr<SkPDFString> identityString = new SkPDFString("Identity");
+ identityString->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Ordering", identityString.get());
+
+ SkRefPtr<SkPDFInt> 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<SkPDFArray> 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<SkPDFInt> 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<SkPDFArray> 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<SkPDFArray> 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<SkPDFName> subType = new SkPDFName("Type1");
subType->unref(); // SkRefPtr and new both took a reference.
insert("Subtype", subType.get());
- SkRefPtr<SkPDFName> baseFont = new SkPDFName(fontName);
+ SkRefPtr<SkPDFName> baseFont = new SkPDFName(fFontInfo.get()->fFontName);
baseFont->unref(); // SkRefPtr and new both took a reference.
insert("BaseFont", baseFont.get());
- SkRefPtr<SkPDFName> encoding = new SkPDFName("WinAnsiEncoding");
+ SkRefPtr<SkPDFArray> 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<SkPDFInt> firstCharInt = new SkPDFInt(firstChar);
+ firstCharInt->unref(); // SkRefPtr and new both took a reference.
+ insert("FirstChar", firstCharInt.get());
+
+ SkRefPtr<SkPDFInt> lastChar =
+ new SkPDFInt(firstChar + widthArray->size() - 1);
+ lastChar->unref(); // SkRefPtr and new both took a reference.
+ insert("LastChar", lastChar.get());
+
+ SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
encoding->unref(); // SkRefPtr and new both took a reference.
insert("Encoding", encoding.get());
+
+ SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+ encDiffs->unref(); // SkRefPtr and new both took a reference.
+ encoding->insert("Differences", encDiffs.get());
+
+ encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+ SkRefPtr<SkPDFInt> 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<SkPDFName> 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<SkPDFObjRef> 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<SkStream> 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<SkPDFStream> fontStream = new SkPDFStream(fontData);
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFInt> headerLen = new SkPDFInt(header);
+ headerLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length1", headerLen.get());
+ SkRefPtr<SkPDFInt> dataLen = new SkPDFInt(data);
+ dataLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length2", dataLen.get());
+ SkRefPtr<SkPDFInt> trailerLen = new SkPDFInt(trailer);
+ trailerLen->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length3", trailerLen.get());
+
+ SkRefPtr<SkPDFObjRef> 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<SkStream> fontData = SkFontHost::OpenStream(fFontID);
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFInt> length = new SkPDFInt(fontData->getLength());
+ length->unref(); // SkRefPtr and new both took a reference.
+ fontStream->insert("Length1", length.get());
+
+ SkRefPtr<SkPDFObjRef> 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<SkStream> fontData = SkFontHost::OpenStream(fFontID);
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ SkRefPtr<SkPDFName> 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<SkPDFObjRef> 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<SkPDFObjRef> descRef = new SkPDFObjRef(fDescriptor.get());
+ descRef->unref(); // SkRefPtr and new both took a reference.
+ insert("FontDescriptor", descRef.get());
+
+ SkRefPtr<SkPDFName> fontName =
+ new SkPDFName(fFontInfo.get()->fFontName);
+ fontName->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("FontName", fontName.get());
+
+ SkRefPtr<SkPDFInt> 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<SkPDFArray> bbox = new SkPDFArray;
+ bbox->unref(); // SkRefPtr and new both took a reference.
+ bbox->reserve(4);
+ SkRefPtr<SkPDFInt> bboxXMin = new SkPDFInt(glyphBBox.fLeft);
+ bboxXMin->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxXMin.get());
+ SkRefPtr<SkPDFInt> bboxYMin = new SkPDFInt(glyphBBox.fBottom);
+ bboxYMin->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxYMin.get());
+ SkRefPtr<SkPDFInt> bboxXMax = new SkPDFInt(glyphBBox.fRight);
+ bboxXMax->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxXMax.get());
+ SkRefPtr<SkPDFInt> bboxYMax = new SkPDFInt(glyphBBox.fTop);
+ bboxYMax->unref(); // SkRefPtr and new both took a reference.
+ bbox->append(bboxYMax.get());
+ fDescriptor->insert("FontBBox", bbox.get());
+
+ SkRefPtr<SkPDFInt> italicAngle =
+ new SkPDFInt(fFontInfo.get()->fItalicAngle);
+ italicAngle->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("ItalicAngle", italicAngle.get());
+
+ SkRefPtr<SkPDFScalar> ascent = new SkPDFScalar(fFontInfo.get()->fAscent);
+ ascent->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("Ascent", ascent.get());
+
+ SkRefPtr<SkPDFScalar> descent = new SkPDFScalar(fFontInfo.get()->fDescent);
+ descent->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("Descent", descent.get());
+
+ SkRefPtr<SkPDFScalar> capHeight =
+ new SkPDFScalar(fFontInfo.get()->fCapHeight);
+ capHeight->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("CapHeight", capHeight.get());
+
+ SkRefPtr<SkPDFScalar> stemV = new SkPDFScalar(fFontInfo.get()->fStemV);
+ stemV->unref(); // SkRefPtr and new both took a reference.
+ fDescriptor->insert("StemV", stemV.get());
+
+ if (defaultWidth > 0) {
+ SkRefPtr<SkPDFInt> 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<SkPDFScalar> 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<SkPDFBool> 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 <ft2build.h>
#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 <typename Data>
+void resetRange(SkPDFTypefaceInfo::AdvanceMetric<Data>* range, int startId) {
+ range->fStartId = startId;
+ range->fAdvance.setCount(0);
+}
+
+template <typename Data>
+SkPDFTypefaceInfo::AdvanceMetric<Data>* appendRange(
+ SkTScopedPtr<SkPDFTypefaceInfo::AdvanceMetric<Data> >* nextSlot,
+ int startId) {
+ nextSlot->reset(new SkPDFTypefaceInfo::AdvanceMetric<Data>);
+ resetRange(nextSlot->get(), startId);
+ return nextSlot->get();
+}
+
+template <typename Data>
+void finishRange(
+ SkPDFTypefaceInfo::AdvanceMetric<Data>* range,
+ int endId,
+ typename SkPDFTypefaceInfo::AdvanceMetric<Data>::MetricType type) {
+ range->fEndId = endId;
+ range->fType = type;
+ int newLength;
+ if (type == SkPDFTypefaceInfo::AdvanceMetric<Data>::kRange)
+ newLength = endId - range->fStartId + 1;
+ else
+ newLength = 1;
+ SkASSERT(range->fAdvance.count() >= newLength);
+ range->fAdvance.setCount(newLength);
+}
+
+template <typename Data>
+SkPDFTypefaceInfo::AdvanceMetric<Data>* 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<SkPDFTypefaceInfo::AdvanceMetric<Data> > result;
+ SkPDFTypefaceInfo::AdvanceMetric<Data>* 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<struct FT_LibraryRec_, FT_Done_FreeType> 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<SkString>(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
@@ -301,6 +301,12 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
}
// static
+SkPDFTypefaceInfo* SkFontHost::GetPDFTypefaceInfo(uint32_t fontID) {
+ SkASSERT(!"SkFontHost::GetPDFTypefaceInfo unimplemented");
+ return NULL;
+}
+
+// static
SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
{
SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented");
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