diff options
-rw-r--r-- | include/core/SkPaint.h | 3 | ||||
-rw-r--r-- | include/core/SkScalar.h | 13 | ||||
-rw-r--r-- | include/core/SkTypes.h | 11 | ||||
-rw-r--r-- | include/pdf/SkPDFDevice.h | 9 | ||||
-rw-r--r-- | include/pdf/SkPDFFont.h | 92 | ||||
-rw-r--r-- | include/pdf/SkPDFTypes.h | 15 | ||||
-rw-r--r-- | include/utils/SkTextFormatParams.h | 48 | ||||
-rw-r--r-- | src/core/Makefile.am | 1 | ||||
-rw-r--r-- | src/core/SkDraw.cpp | 5 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 50 | ||||
-rw-r--r-- | src/core/SkScalar.cpp | 42 | ||||
-rw-r--r-- | src/core/core_files.mk | 1 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 255 | ||||
-rw-r--r-- | src/pdf/SkPDFFont.cpp | 199 | ||||
-rw-r--r-- | src/pdf/SkPDFGraphicState.cpp | 26 | ||||
-rw-r--r-- | src/pdf/SkPDFTypes.cpp | 57 | ||||
-rw-r--r-- | src/pdf/pdf_files.mk | 1 | ||||
-rw-r--r-- | src/ports/SkThread_win.cpp | 15 |
18 files changed, 719 insertions, 124 deletions
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 722bc5d851..16411d5886 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -863,9 +863,10 @@ private: enum { kCanonicalTextSizeForPaths = 64 }; + friend class SkAutoGlyphCache; friend class SkCanvas; friend class SkDraw; - friend class SkAutoGlyphCache; + friend class SkPDFDevice; friend class SkTextToPathIter; }; diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h index 9130a7cf12..e1a8a33b27 100644 --- a/include/core/SkScalar.h +++ b/include/core/SkScalar.h @@ -253,5 +253,18 @@ static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) { return A + SkScalarMul(B - A, t); } +/** Interpolate along the function described by (keys[length], values[length]) + for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] + clamp to the min or max value. This function was inspired by a desire + to change the multiplier for thickness in fakeBold; therefore it assumes + the number of pairs (length) will be small, and a linear search is used. + Repeated keys are allowed for discontinuous functions (so long as keys is + monotonically increasing), and if key is the value of a repeated scalar in + keys, the first one will be used. However, that may change if a binary + search is used. +*/ +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length); + #endif diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h index 1f8ecaaf63..85f542136e 100644 --- a/include/core/SkTypes.h +++ b/include/core/SkTypes.h @@ -103,6 +103,17 @@ static inline void sk_bzero(void* buffer, size_t size) { #define SkAssertResult(cond) cond #endif +namespace { + +template <bool> +struct SkCompileAssert { +}; + +} // namespace + +#define SK_COMPILE_ASSERT(expr, msg) \ + typedef SkCompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + /////////////////////////////////////////////////////////////////////// /** Fast type for signed 8 bits. Use for parameter passing and local variables, not for storage diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h index ada2791ec6..b92dc3db2b 100644 --- a/include/pdf/SkPDFDevice.h +++ b/include/pdf/SkPDFDevice.h @@ -26,6 +26,7 @@ class SkPDFArray; class SkPDFDevice; class SkPDFDict; +class SkPDFFont; class SkPDFGraphicState; class SkPDFObject; class SkPDFStream; @@ -124,6 +125,7 @@ private: SkTDArray<SkPDFGraphicState*> fGraphicStateResources; SkTDArray<SkPDFObject*> fXObjectResources; + SkTDArray<SkPDFFont*> fFontResources; // In PDF, transforms and clips can only be undone by popping the graphic // state to before the transform or clip was applied. Because it can be @@ -138,7 +140,10 @@ private: // two entries: a clip and then a transform struct GraphicStackEntry { SkColor fColor; + SkScalar fTextSize; SkScalar fTextScaleX; + SkPaint::Style fTextFill; + SkRefPtr<SkPDFFont> fFont; SkRefPtr<SkPDFGraphicState> fGraphicState; SkRegion fClip; SkMatrix fTransform; @@ -148,7 +153,8 @@ private: SkString fContent; - void updateGSFromPaint(const SkPaint& newPaint, SkString* textStaetUpdate); + void updateGSFromPaint(const SkPaint& newPaint, bool forText); + int getFontResourceIndex(uint32_t fontID); void moveTo(SkScalar x, SkScalar y); void appendLine(SkScalar x, SkScalar y); @@ -162,6 +168,7 @@ private: void strokePath(); void pushGS(); void popGS(); + void setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX); void internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap, const SkPaint& paint); diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h new file mode 100644 index 0000000000..82357ef355 --- /dev/null +++ b/include/pdf/SkPDFFont.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef SkPDFFont_DEFINED +#define SkPDFFont_DEFINED + +#include "SkPDFTypes.h" +#include "SkTDArray.h" +#include "SkThread.h" + +class SkPaint; + +/** \class SkPDFFont + A PDF Object class representing a font. The font may have resources + attached to it in order to embed the font. SkPDFFonts are canonicalized + so that resource deduplication will only include one copy of a font. + This class uses the same pattern as SkPDFGraphicState, a static weak + reference to each instantiated class. +*/ +class SkPDFFont : public SkPDFDict { +public: + virtual ~SkPDFFont(); + + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + /* Returns the font ID represented by this class. + */ + uint32_t fontID(); + + /** Returns true if this font 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. + */ + int textToPDFGlyphs(const void* text, size_t byteLength, + const SkPaint& paint, uint16_t* glyphs, + size_t glyphsLength) const; + + /** Get the font resource for the passed font ID. The reference count of + * the object is incremented and it is the caller's responsibility to + * unreference it when done. This is needed to accommodate the weak + * reference pattern used when the returned object is new and has no + * other references. + * @param paint The SkPaint to emulate. + */ + static SkPDFFont* getFontResouceByID(uint32_t fontID); + +private: + uint32_t fFontID; + bool fMultiByteGlyphs; + + SkTDArray<SkPDFObject*> fResources; + + class FontRec { + public: + SkPDFFont* fFont; + uint32_t fFontID; + + bool operator==(const FontRec& b) const; + FontRec(SkPDFFont* font, uint32_t fontID); + }; + + // 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); + + void populateBuiltinFont(const char fontName[]); + void populateFont(const char subType[], const char fontName[], + int firstChar, int lastChar, int widths[], + SkPDFObject* fontDescriptor); + + static int find(uint32_t fontID); +}; + +#endif diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h index a281628a23..fc9b62c30a 100644 --- a/include/pdf/SkPDFTypes.h +++ b/include/pdf/SkPDFTypes.h @@ -21,6 +21,7 @@ #include "SkScalar.h" #include "SkString.h" #include "SkTDArray.h" +#include "SkTypes.h" class SkPDFCatalog; class SkWStream; @@ -169,6 +170,14 @@ public: */ explicit SkPDFString(const char value[]); explicit SkPDFString(const SkString& value); + + /** Create a PDF string. Maximum length (in bytes) is 65,535. + * @param value A string value. + * @param len The length of value. + * @param wideChars Indicates if the top byte in value is significant and + * should be encoded (true) or not (false). + */ + SkPDFString(const uint16_t* value, size_t len, bool wideChars); virtual ~SkPDFString(); // The SkPDFObject interface. @@ -176,12 +185,16 @@ public: bool indirect); virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + static SkString formatString(const char* input, size_t len); + static SkString formatString(const uint16_t* input, size_t len, + bool wideChars); private: static const size_t kMaxLen = 65535; const SkString fValue; - SkString formatString(const SkString& input); + static SkString doFormatString(const void* input, size_t len, + bool wideInput, bool wideOutput); }; /** \class SkPDFName diff --git a/include/utils/SkTextFormatParams.h b/include/utils/SkTextFormatParams.h new file mode 100644 index 0000000000..3306270788 --- /dev/null +++ b/include/utils/SkTextFormatParams.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef SkTextFormatParams_DEFINES +#define SkTextFormatParams_DEFINES + +#include "SkScalar.h" +#include "SkTypes.h" + +// Fraction of the text size to lower a strike through line below the baseline. +#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) +// Fraction of the text size to lower a underline below the baseline. +#define kStdUnderline_Offset (SK_Scalar1 / 9) +// Fraction of the text size to use for a strike through or under-line. +#define kStdUnderline_Thickness (SK_Scalar1 / 18) + +// The fraction of text size to embolden fake bold text scales with text size. +// At 9 points or below, the stroke width is increased by text size / 24. +// At 36 points and above, it is increased by text size / 32. In between, +// it is interpolated between those values. +static const SkScalar kStdFakeBoldInterpKeys[] = { + SkIntToScalar(9), + SkIntToScalar(36) +}; +static const SkScalar kStdFakeBoldInterpValues[] = { + SK_Scalar1/24, + SK_Scalar1/32 +}; +SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kStdFakeBoldInterpKeys) == + SK_ARRAY_COUNT(kStdFakeBoldInterpValues), + mismatched_array_size); +static const int kStdFakeBoldInterpLength = + SK_ARRAY_COUNT(kStdFakeBoldInterpKeys); + +#endif //SkTextFormatParams_DEFINES diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 05fc9bb119..4adde0a4c1 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -69,6 +69,7 @@ SkRect.cpp \ SkRefCnt.cpp \ SkRegion.cpp \ SkRegion_path.cpp \ +SkScalar.cpp \ SkScalerContext.cpp \ SkScan.cpp \ SkScan_AntiPath.cpp \ diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 8671d74d70..eff2b32a4c 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -29,6 +29,7 @@ #include "SkShader.h" #include "SkStroke.h" #include "SkTemplatesPriv.h" +#include "SkTextFormatParams.h" #include "SkUtils.h" #include "SkAutoKern.h" @@ -1224,10 +1225,6 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength, } } -#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) -#define kStdUnderline_Offset (SK_Scalar1 / 9) -#define kStdUnderline_Thickness (SK_Scalar1 / 18) - static void draw_paint_rect(const SkDraw* draw, const SkPaint& paint, const SkRect& r, SkScalar textSize) { if (paint.getStyle() == SkPaint::kFill_Style) { diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 21b7534f9b..43a6892049 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -23,8 +23,10 @@ #include "SkPathEffect.h" #include "SkRasterizer.h" #include "SkShader.h" +#include "SkScalar.h" #include "SkScalerContext.h" #include "SkStroke.h" +#include "SkTextFormatParams.h" #include "SkTypeface.h" #include "SkXfermode.h" #include "SkAutoKern.h" @@ -1180,49 +1182,6 @@ static void add_flattenable(SkDescriptor* desc, uint32_t tag, buffer->flatten(desc->addEntry(tag, buffer->size(), NULL)); } -/* - * interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i]) - inspired by a desire to change the multiplier for thickness in fakebold - therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient - repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if - key is the value of a repeated scalar in keys, the first one will be used - - this may change if a binary search is used - - also, this ensures that there is no divide by zero (an assert also checks for that) -*/ -static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length) -{ - - SkASSERT(length > 0); - SkASSERT(keys != NULL); - SkASSERT(values != NULL); -#ifdef SK_DEBUG - for (int i = 1; i < length; i++) - SkASSERT(keys[i] >= keys[i-1]); -#endif - int right = 0; - while (right < length && key > keys[right]) - right++; - //could use sentinal values to eliminate conditionals - //i assume i am not in control of input values, so i want to make it simple - if (length == right) - return values[length-1]; - if (0 == right) - return values[0]; - //otherwise, we interpolate between right-1 and right - SkScalar rVal = values[right]; - SkScalar lVal = values[right-1]; - SkScalar rightKey = keys[right]; - SkScalar leftKey = keys[right-1]; - SkASSERT(rightKey != leftKey); - //fractional amount which we will multiply by the difference in the left value and right value - SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey); - return lVal + SkScalarMul(fract, rVal-lVal); -} - -//used for interpolating in fakeBold -static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) }; -static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 }; - static SkMask::Format computeMaskFormat(const SkPaint& paint) { uint32_t flags = paint.getFlags(); @@ -1284,7 +1243,10 @@ void SkScalerContext::MakeRec(const SkPaint& paint, #ifdef SK_USE_FREETYPE_EMBOLDEN flags |= SkScalerContext::kEmbolden_Flag; #else - SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2); + SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(), + kStdFakeBoldInterpKeys, + kStdFakeBoldInterpValues, + kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale); if (style == SkPaint::kFill_Style) diff --git a/src/core/SkScalar.cpp b/src/core/SkScalar.cpp new file mode 100644 index 0000000000..c6755d1d3c --- /dev/null +++ b/src/core/SkScalar.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2010, 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 "SkScalar.h" + +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length) { + SkASSERT(length > 0); + SkASSERT(keys != NULL); + SkASSERT(values != NULL); +#ifdef SK_DEBUG + for (int i = 1; i < length; i++) + SkASSERT(keys[i] >= keys[i-1]); +#endif + int right = 0; + while (right < length && searchKey > keys[right]) + right++; + // Could use sentinel values to eliminate conditionals, but since the + // tables are taken as input, a simpler format is better. + if (length == right) + return values[length-1]; + if (0 == right) + return values[0]; + // Otherwise, interpolate between right - 1 and right. + SkScalar rightKey = keys[right]; + SkScalar leftKey = keys[right-1]; + SkScalar fract = SkScalarDiv(searchKey-leftKey,rightKey-leftKey); + return SkScalarInterp(values[right-1], values[right], fract); +} diff --git a/src/core/core_files.mk b/src/core/core_files.mk index d5cdf39a1c..e84365a7cb 100644 --- a/src/core/core_files.mk +++ b/src/core/core_files.mk @@ -72,6 +72,7 @@ SOURCE := \ SkRefCnt.cpp \ SkRegion.cpp \ SkRegion_path.cpp \ + SkScalar.cpp \ SkScalerContext.cpp \ SkScan.cpp \ SkScan_AntiPath.cpp \ diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index e9625cf6a6..cc65413328 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -17,15 +17,20 @@ #include "SkPDFDevice.h" #include "SkColor.h" +#include "SkGlyphCache.h" #include "SkPaint.h" #include "SkPath.h" #include "SkPDFImage.h" #include "SkPDFGraphicState.h" +#include "SkPDFFont.h" #include "SkPDFFormXObject.h" #include "SkPDFTypes.h" #include "SkPDFStream.h" #include "SkRect.h" #include "SkString.h" +#include "SkTextFormatParams.h" +#include "SkTypeface.h" +#include "SkTypes.h" #define NOT_IMPLEMENTED(condition, assert) \ do { \ @@ -55,6 +60,60 @@ SkString toPDFColor(SkColor color) { return result; } +SkPaint calculateTextPaint(const SkPaint& paint) { + SkPaint result = paint; + if (result.isFakeBoldText()) { + SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), + kStdFakeBoldInterpKeys, + kStdFakeBoldInterpValues, + kStdFakeBoldInterpLength); + SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale); + if (result.getStyle() == SkPaint::kFill_Style) + result.setStyle(SkPaint::kStrokeAndFill_Style); + else + width += result.getStrokeWidth(); + result.setStrokeWidth(width); + } + return result; +} + +// Stolen from measure_text in SkDraw.cpp and then tweaked. +void alignText(SkDrawCacheProc glyphCacheProc, const SkPaint& paint, + const uint16_t* glyphs, size_t len, SkScalar* x, SkScalar* y, + SkScalar* width) { + if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL) + return; + + SkMatrix ident; + ident.reset(); + SkAutoGlyphCache autoCache(paint, &ident); + SkGlyphCache* cache = autoCache.getCache(); + + const char* start = (char*)glyphs; + const char* stop = (char*)(glyphs + len); + SkFixed xAdv = 0, yAdv = 0; + + // TODO(vandebo) This probably needs to take kerning into account. + while (start < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0); + xAdv += glyph.fAdvanceX; + yAdv += glyph.fAdvanceY; + }; + if (width) + *width = SkFixedToScalar(xAdv); + if (paint.getTextAlign() == SkPaint::kLeft_Align) + return; + + SkScalar xAdj = SkFixedToScalar(xAdv); + SkScalar yAdj = SkFixedToScalar(yAdv); + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + xAdj = SkScalarHalf(xAdj); + yAdj = SkScalarHalf(yAdj); + } + *x = *x - xAdj; + *y = *y - yAdj; +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -70,7 +129,9 @@ SkPDFDevice::SkPDFDevice(int width, int height) fHeight(height), fGraphicStackIndex(0) { fGraphicStack[0].fColor = SK_ColorBLACK; + fGraphicStack[0].fTextSize = SK_ScalarNaN; // This has no default value. fGraphicStack[0].fTextScaleX = SK_Scalar1; + fGraphicStack[0].fTextFill = SkPaint::kFill_Style; fGraphicStack[0].fClip.setRect(0,0, width, height); fGraphicStack[0].fTransform.reset(); } @@ -78,6 +139,7 @@ SkPDFDevice::SkPDFDevice(int width, int height) SkPDFDevice::~SkPDFDevice() { fGraphicStateResources.unrefAll(); fXObjectResources.unrefAll(); + fFontResources.unrefAll(); } void SkPDFDevice::setMatrixClip(const SkMatrix& matrix, @@ -113,7 +175,7 @@ void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { SkPaint newPaint = paint; newPaint.setStyle(SkPaint::kFill_Style); - updateGSFromPaint(newPaint, NULL); + updateGSFromPaint(newPaint, false); SkRect all = SkRect::MakeWH(width() + 1, height() + 1); drawRect(d, all, newPaint); @@ -128,14 +190,14 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, switch (mode) { case SkCanvas::kPolygon_PointMode: - updateGSFromPaint(paint, NULL); + updateGSFromPaint(paint, false); moveTo(points[0].fX, points[0].fY); for (size_t i = 1; i < count; i++) appendLine(points[i].fX, points[i].fY); strokePath(); break; case SkCanvas::kLines_PointMode: - updateGSFromPaint(paint, NULL); + updateGSFromPaint(paint, false); for (size_t i = 0; i < count/2; i++) { moveTo(points[i * 2].fX, points[i * 2].fY); appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY); @@ -144,7 +206,7 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, break; case SkCanvas::kPoints_PointMode: if (paint.getStrokeCap() == SkPaint::kRound_Cap) { - updateGSFromPaint(paint, NULL); + updateGSFromPaint(paint, false); for (size_t i = 0; i < count; i++) { moveTo(points[i].fX, points[i].fY); strokePath(); @@ -182,7 +244,7 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r, drawPath(d, path, noEffectPaint); return; } - updateGSFromPaint(paint, NULL); + updateGSFromPaint(paint, false); // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop; @@ -202,7 +264,7 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path, drawPath(d, noEffectPath, noEffectPaint); return; } - updateGSFromPaint(paint, NULL); + updateGSFromPaint(paint, false); emitPath(path); paintPath(paint.getStyle(), path.getFillType()); @@ -222,15 +284,77 @@ void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, internalDrawBitmap(matrix, bitmap, paint); } -void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len, +void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) { - NOT_IMPLEMENTED("drawText", true); + 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); + + SkScalar width; + SkScalar* widthPtr = NULL; + if (textPaint.isUnderlineText() || textPaint.isStrikeThruText()) + widthPtr = &width; + + SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); + alignText(glyphCacheProc, textPaint, glyphs, glyphsLength, &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"); + + // Draw underline and/or strikethrough if the paint has them. + // drawPosText() and drawTextOnPath() don't draw underline or strikethrough + // because the raster versions don't. Use paint instead of textPaint + // because we may have changed strokeWidth to do fakeBold text. + if (paint.isUnderlineText() || paint.isStrikeThruText()) { + SkScalar textSize = paint.getTextSize(); + SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); + + if (paint.isUnderlineText()) { + SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y); + SkRect r = SkRect::MakeXYWH(x, top - height, width, height); + drawRect(d, r, paint); + } + if (paint.isStrikeThruText()) { + SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y); + SkRect r = SkRect::MakeXYWH(x, top - height, width, height); + drawRect(d, r, paint); + } + } } void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len, const SkScalar pos[], SkScalar constY, int scalarsPerPos, const SkPaint& paint) { - NOT_IMPLEMENTED("drawPosText", false); + 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); + + SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); + fContent.append("BT\n"); + for (size_t i = 0; i < glyphsLength; i++) { + SkScalar x = pos[i * scalarsPerPos]; + SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; + alignText(glyphCacheProc, textPaint, glyphs + i, 1, &x, &y, NULL); + setTextTransform(x, y, textPaint.getTextSkewX()); + fContent.append(SkPDFString::formatString(glyphs + i, 1, + font->multiByteGlyphs())); + fContent.append(" Tj\n"); + } + fContent.append("ET\n"); } void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, @@ -304,6 +428,35 @@ const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() { } fResourceDict->insert("XObject", xObjects.get()); } + + if (fFontResources.count()) { + SkRefPtr<SkPDFDict> fonts = new SkPDFDict(); + fonts->unref(); // SkRefPtr and new both took a reference. + for (int i = 0; i < fFontResources.count(); i++) { + SkString nameString("F"); + nameString.appendS32(i); + SkRefPtr<SkPDFName> name = new SkPDFName(nameString); + name->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFObjRef> fontRef = + new SkPDFObjRef(fFontResources[i]); + fontRef->unref(); // SkRefPtr and new both took a reference. + fonts->insert(name.get(), fontRef.get()); + } + fResourceDict->insert("Font", fonts.get()); + } + + // For compatibility, add all proc sets (only used for output to PS + // devices). + const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; + SkRefPtr<SkPDFArray> procSets = new SkPDFArray(); + procSets->unref(); // SkRefPtr and new both took a reference. + procSets->reserve(SK_ARRAY_COUNT(procs)); + for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) { + SkRefPtr<SkPDFName> entry = new SkPDFName(procs[i]); + entry->unref(); // SkRefPtr and new both took a reference. + procSets->append(entry.get()); + } + fResourceDict->insert("ProcSet", procSets.get()); } return fResourceDict; } @@ -311,7 +464,8 @@ const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() { void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const { resourceList->setReserve(resourceList->count() + fGraphicStateResources.count() + - fXObjectResources.count()); + fXObjectResources.count() + + fFontResources.count()); for (int i = 0; i < fGraphicStateResources.count(); i++) { resourceList->push(fGraphicStateResources[i]); fGraphicStateResources[i]->ref(); @@ -322,6 +476,11 @@ void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const { fXObjectResources[i]->ref(); fXObjectResources[i]->getResources(resourceList); } + for (int i = 0; i < fFontResources.count(); i++) { + resourceList->push(fFontResources[i]); + fFontResources[i]->ref(); + fFontResources[i]->getResources(resourceList); + } } SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const { @@ -358,17 +517,12 @@ SkString SkPDFDevice::content(bool flipOrigin) const { // TODO(vandebo) handle these cases. #define PAINTCHECK(x,y) NOT_IMPLEMENTED(newPaint.x() y, false) -void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, - SkString* textStateUpdate) { +void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, bool forText) { PAINTCHECK(getXfermode, != NULL); PAINTCHECK(getPathEffect, != NULL); PAINTCHECK(getMaskFilter, != NULL); PAINTCHECK(getShader, != NULL); PAINTCHECK(getColorFilter, != NULL); - PAINTCHECK(isFakeBoldText, == true); - PAINTCHECK(isUnderlineText, == true); - PAINTCHECK(isStrikeThruText, == true); - PAINTCHECK(getTextSkewX, != 0); SkRefPtr<SkPDFGraphicState> newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint); @@ -400,17 +554,58 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint, fGraphicStack[fGraphicStackIndex].fColor = newColor; } - if (textStateUpdate != NULL && - fGraphicStack[fGraphicStackIndex].fTextScaleX != - newPaint.getTextScaleX()) { - SkScalar scale = newPaint.getTextScaleX(); - SkScalar pdfScale = scale * 100; - textStateUpdate->appendScalar(pdfScale); - textStateUpdate->append(" Tz\n"); - fGraphicStack[fGraphicStackIndex].fTextScaleX = scale; + 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(); + SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100)); + fContent.appendScalar(pdfScale); + fContent.append(" Tz\n"); + fGraphicStack[fGraphicStackIndex].fTextScaleX = scale; + } + + if (fGraphicStack[fGraphicStackIndex].fTextFill != + newPaint.getStyle()) { + SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value); + SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1, + enum_must_match_value); + SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2, + enum_must_match_value); + fContent.appendS32(newPaint.getStyle()); + fContent.append(" Tr\n"); + fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle(); + } } } +int SkPDFDevice::getFontResourceIndex(uint32_t fontID) { + SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResouceByID(fontID); + newFont->unref(); // getFontResourceByID and SkRefPtr both took a ref. + int resourceIndex = fFontResources.find(newFont.get()); + if (resourceIndex < 0) { + resourceIndex = fFontResources.count(); + fFontResources.push(newFont.get()); + newFont->ref(); + } + return resourceIndex; +} + void SkPDFDevice::moveTo(SkScalar x, SkScalar y) { fContent.appendScalar(x); fContent.append(" "); @@ -538,6 +733,18 @@ void SkPDFDevice::popGS() { fGraphicStackIndex--; } +void SkPDFDevice::setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX) { + // Flip the text about the x-axis to account for origin swap and include + // the passed parameters. + fContent.append("1 0 "); + fContent.appendScalar(0 - textSkewX); + fContent.append(" -1 "); + fContent.appendScalar(x); + fContent.append(" "); + fContent.appendScalar(y); + fContent.append(" Tm\n"); +} + void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap, const SkPaint& paint) { diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp new file mode 100644 index 0000000000..1bbc53af4e --- /dev/null +++ b/src/pdf/SkPDFFont.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 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 "SkPaint.h" +#include "SkPDFFont.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; +} + +} + +SkPDFFont::~SkPDFFont() { + SkAutoMutexAcquire lock(canonicalFontsMutex()); + int index = find(fFontID); + SkASSERT(index >= 0); + canonicalFonts().removeShuffle(index); +} + +void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + fResources[i]->getResources(resourceList); + } +} + +uint32_t SkPDFFont::fontID() { + return fFontID; +} + +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); + } + } + + 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; +} + +// static +SkPDFFont* SkPDFFont::getFontResouceByID(uint32_t fontID) { + SkAutoMutexAcquire lock(canonicalFontsMutex()); + int index = find(fontID); + if (index >= 0) { + 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"); + + FontRec newEntry(font, fontID); + canonicalFonts().push(newEntry); + return font; // Return the reference new SkPDFFont() created. +} + +// static +SkTDArray<SkPDFFont::FontRec>& SkPDFFont::canonicalFonts() { + // This initialization is only thread safe with gcc. + static SkTDArray<FontRec> gCanonicalFonts; + return gCanonicalFonts; +} + +// static +SkMutex& SkPDFFont::canonicalFontsMutex() { + // This initialization is only thread safe with gcc. + static SkMutex gCanonicalFontsMutex; + return gCanonicalFontsMutex; +} + +// static +int SkPDFFont::find(uint32_t fontID) { + FontRec search(NULL, fontID); + return canonicalFonts().find(search); +} + +SkPDFFont::SkPDFFont(uint32_t fontID, bool multiByteGlyphs) + : fFontID(fontID), + fMultiByteGlyphs(multiByteGlyphs) { +} + +void SkPDFFont::populateBuiltinFont(const char fontName[]) { + SkASSERT(strcmp(fontName, "Time-Roman") == 0 || + strcmp(fontName, "Time-Bold") == 0 || + strcmp(fontName, "Time-Italic") == 0 || + strcmp(fontName, "Time-BoldItalic") == 0 || + strcmp(fontName, "Helvetica") == 0 || + strcmp(fontName, "Helvetica-Bold") == 0 || + strcmp(fontName, "Helvetica-Oblique") == 0 || + strcmp(fontName, "Helvetica-BoldOblique") == 0 || + strcmp(fontName, "Courier") == 0 || + strcmp(fontName, "Courier-Bold") == 0 || + strcmp(fontName, "Courier-Oblique") == 0 || + strcmp(fontName, "Courier-BoldOblique") == 0 || + strcmp(fontName, "Symbol") == 0 || + strcmp(fontName, "ZapfDingbats") == 0); + + SkRefPtr<SkPDFName> type = new SkPDFName("Font"); + type->unref(); // SkRefPtr and new both took a reference. + insert("Type", type.get()); + + 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); + baseFont->unref(); // SkRefPtr and new both took a reference. + insert("BaseFont", baseFont.get()); + + SkRefPtr<SkPDFName> encoding = new SkPDFName("WinAnsiEncoding"); + encoding->unref(); // SkRefPtr and new both took a reference. + insert("Encoding", encoding.get()); +} + +void SkPDFFont::populateFont(const char subType[], const char fontName[], + int firstChar, int lastChar, int widths[], + SkPDFObject* fontDescriptor) { +} + +bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const { + return fFontID == b.fFontID; +} + +SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID) + : fFont(font), + fFontID(fontID) { +} diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp index d0d47b5e6f..1fb62fa199 100644 --- a/src/pdf/SkPDFGraphicState.cpp +++ b/src/pdf/SkPDFGraphicState.cpp @@ -16,7 +16,6 @@ #include "SkPDFGraphicState.h" #include "SkStream.h" -#include "SkTypeface.h" SkPDFGraphicState::~SkPDFGraphicState() { SkAutoMutexAcquire lock(canonicalPaintsMutex()); @@ -107,24 +106,6 @@ void SkPDFGraphicState::populateDict() { strokeJoin->unref(); // SkRefPtr and new both took a reference. insert("LJ", strokeJoin.get()); - /* TODO(vandebo) Font. - if (fPaint.getTypeFace() != NULL) { - SkRefPtr<SkPDFTypeFace> typeFace = - SkPDFTypeFace::getFontForTypeFace(fPaint.getTypeFace); - SkRefPtr<SkPDFObjRef> typeFaceRef = new SkPDFObjRef(typeFace.get()); - fontRef->unref(); // SkRefPtr and new both took a reference. - SkRefPtr<SkPDFScalar> fontSize = - new SkPDFScalar(fPaint.getTetSize()); - fontSize->unref(); // SkRefPtr and new both took a reference. - SkRefPtr<SkPDFArray> font = new SkPDFArray(); - font->unref(); // SkRefPtr and new both took a reference. - font->reserve(2); - font->append(typeFaceRef.get()); - font->append(fontSize.get()); - insert("LJ", font.get()); - } - */ - SkRefPtr<SkPDFScalar> strokeWidth = new SkPDFScalar(fPaint.getStrokeWidth()); strokeWidth->unref(); // SkRefPtr and new both took a reference. @@ -150,14 +131,9 @@ bool SkPDFGraphicState::GSCanonicalEntry::operator==( const SkPaint* b = gs.fPaint; SkASSERT(a != NULL); SkASSERT(b != NULL); - SkTypeface* aFace = a->getTypeface(); - SkTypeface* bFace = b->getTypeface(); return SkColorGetA(a->getColor()) == SkColorGetA(b->getColor()) && a->getStrokeCap() == b->getStrokeCap() && a->getStrokeJoin() == b->getStrokeJoin() && - a->getTextSize() == b->getTextSize() && a->getStrokeWidth() == b->getStrokeWidth() && - a->getStrokeMiter() == b->getStrokeMiter() && - (aFace == NULL) == (bFace == NULL) && - (aFace == NULL || aFace->uniqueID() == bFace->uniqueID()); + a->getStrokeMiter() == b->getStrokeMiter(); } diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index 8bff735da4..9649f0baec 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -97,11 +97,15 @@ void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog, } SkPDFString::SkPDFString(const char value[]) - : fValue(formatString(SkString(value))) { + : fValue(formatString(value, sizeof(value))) { } SkPDFString::SkPDFString(const SkString& value) - : fValue(formatString(value)) { + : fValue(formatString(value.c_str(), value.size())) { +} + +SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars) + : fValue(formatString(value, len, wideChars)) { } SkPDFString::~SkPDFString() {} @@ -119,14 +123,40 @@ size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) { return fValue.size(); } -SkString SkPDFString::formatString(const SkString& input) { - SkASSERT(input.size() <= kMaxLen); +// static +SkString SkPDFString::formatString(const char* input, size_t len) { + return doFormatString(input, len, false, false); +} + +SkString SkPDFString::formatString(const uint16_t* input, size_t len, + bool wideChars) { + return doFormatString(input, len, true, wideChars); +} + +// static +SkString SkPDFString::doFormatString(const void* input, size_t len, + bool wideInput, bool wideOutput) { + SkASSERT(len <= kMaxLen); + const uint16_t* win = (const uint16_t*) input; + const char* cin = (const char*) input; + + if (wideOutput) { + SkASSERT(wideInput); + SkString result; + result.append("<"); + for (size_t i = 0; i < len; i++) + result.appendHex(win[i], 4); + result.append(">"); + return result; + } // 7-bit clean is a heuristic to decide what string format to use; // a 7-bit clean string should require little escaping. bool sevenBitClean = true; - for (size_t i = 0; i < input.size(); i++) { - if (input[i] & 0x80 || input[i] < ' ') { + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + char val = wideInput ? win[i] : cin[i]; + if (val & 0x80 || val < ' ') { sevenBitClean = false; break; } @@ -135,16 +165,21 @@ SkString SkPDFString::formatString(const SkString& input) { SkString result; if (sevenBitClean) { result.append("("); - for (size_t i = 0; i < input.size(); i++) { - if (input[i] == '\\' || input[i] == '(' || input[i] == ')') + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + char val = wideInput ? win[i] : cin[i]; + if (val == '\\' || val == '(' || val == ')') result.append("\\"); - result.append(input.c_str() + i, 1); + result.append(&val, 1); } result.append(")"); } else { result.append("<"); - for (size_t i = 0; i < input.size(); i++) - result.appendHex(input[i], 2); + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + char val = wideInput ? win[i] : cin[i]; + result.appendHex(val, 2); + } result.append(">"); } diff --git a/src/pdf/pdf_files.mk b/src/pdf/pdf_files.mk index 86b3e0dd3b..8d721eb816 100644 --- a/src/pdf/pdf_files.mk +++ b/src/pdf/pdf_files.mk @@ -2,6 +2,7 @@ SOURCE := \ SkPDFCatalog.cpp \ SkPDFDevice.cpp \ SkPDFDocument.cpp \ + SkPDFFont.cpp \ SkPDFFormXObject.cpp \ SkPDFGraphicState.cpp \ SkPDFImage.cpp \ diff --git a/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp index d3f3e214be..cb3aa37dfd 100644 --- a/src/ports/SkThread_win.cpp +++ b/src/ports/SkThread_win.cpp @@ -18,17 +18,6 @@ #include <windows.h> #include "SkThread.h" -namespace { - -template <bool> -struct CompileAssert { -}; - -} // namespace - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] - int32_t sk_atomic_inc(int32_t* addr) { // InterlockedIncrement returns the new value, we want to return the old. @@ -42,8 +31,8 @@ int32_t sk_atomic_dec(int32_t* addr) SkMutex::SkMutex(bool /* isGlobal */) { - COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), - NotEnoughSizeForCriticalSection); + SK_COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), + NotEnoughSizeForCriticalSection); InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); } |