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:
parent
c921843d85
commit
2a22e10ab2
@ -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);
|
||||
|
83
include/core/SkTScopedPtr.h
Normal file
83
include/core/SkTScopedPtr.h
Normal 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
|
@ -59,6 +59,7 @@ private:
|
||||
T* fObj;
|
||||
};
|
||||
|
||||
// See also SkTScopedPtr.
|
||||
template <typename T> class SkAutoTDelete : SkNoncopyable {
|
||||
public:
|
||||
SkAutoTDelete(T* obj) : fObj(obj) {}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
122
include/pdf/SkPDFTypefaceInfo.h
Normal file
122
include/pdf/SkPDFTypefaceInfo.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(">");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user