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
This commit is contained in:
vandebo@chromium.org 2011-01-25 21:01:34 +00:00
parent c921843d85
commit 2a22e10ab2
23 changed files with 1523 additions and 193 deletions

View File

@ -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);

View File

@ -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

View File

@ -59,6 +59,7 @@ private:
T* fObj;
};
// See also SkTScopedPtr.
template <typename T> class SkAutoTDelete : SkNoncopyable {
public:
SkAutoTDelete(T* obj) : fObj(obj) {}

View File

@ -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
*/

View File

@ -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);

View File

@ -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<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

View File

@ -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

View File

@ -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);
}

View File

@ -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<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();

View File

@ -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();

View File

@ -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;
// 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<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);
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<SkPDFName> type = new SkPDFName("Font");
type->unref(); // SkRefPtr and new both took a reference.
insert("Type", type.get());
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) {
}

View File

@ -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);

View File

@ -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(">");

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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)

View File

@ -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) {
}

View File

@ -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);

View File

@ -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

View File

@ -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