• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkPDFDevice.h"
18 
19 #include "SkColor.h"
20 #include "SkClipStack.h"
21 #include "SkDraw.h"
22 #include "SkGlyphCache.h"
23 #include "SkPaint.h"
24 #include "SkPath.h"
25 #include "SkPDFFont.h"
26 #include "SkPDFFormXObject.h"
27 #include "SkPDFGraphicState.h"
28 #include "SkPDFImage.h"
29 #include "SkPDFShader.h"
30 #include "SkPDFStream.h"
31 #include "SkPDFTypes.h"
32 #include "SkPDFUtils.h"
33 #include "SkRect.h"
34 #include "SkString.h"
35 #include "SkTextFormatParams.h"
36 #include "SkTypeface.h"
37 #include "SkTypes.h"
38 
39 // Utility functions
40 
emit_pdf_color(SkColor color,SkWStream * result)41 static void emit_pdf_color(SkColor color, SkWStream* result) {
42     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
43     SkScalar colorMax = SkIntToScalar(0xFF);
44     SkPDFScalar::Append(
45             SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
46     result->writeText(" ");
47     SkPDFScalar::Append(
48             SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
49     result->writeText(" ");
50     SkPDFScalar::Append(
51             SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
52     result->writeText(" ");
53 }
54 
calculate_text_paint(const SkPaint & paint)55 static SkPaint calculate_text_paint(const SkPaint& paint) {
56     SkPaint result = paint;
57     if (result.isFakeBoldText()) {
58         SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
59                                                     kStdFakeBoldInterpKeys,
60                                                     kStdFakeBoldInterpValues,
61                                                     kStdFakeBoldInterpLength);
62         SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
63         if (result.getStyle() == SkPaint::kFill_Style)
64             result.setStyle(SkPaint::kStrokeAndFill_Style);
65         else
66             width += result.getStrokeWidth();
67         result.setStrokeWidth(width);
68     }
69     return result;
70 }
71 
72 // Stolen from measure_text in SkDraw.cpp and then tweaked.
align_text(SkDrawCacheProc glyphCacheProc,const SkPaint & paint,const uint16_t * glyphs,size_t len,SkScalar * x,SkScalar * y,SkScalar * width)73 static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
74                        const uint16_t* glyphs, size_t len, SkScalar* x,
75                        SkScalar* y, SkScalar* width) {
76     if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
77         return;
78 
79     SkMatrix ident;
80     ident.reset();
81     SkAutoGlyphCache autoCache(paint, &ident);
82     SkGlyphCache* cache = autoCache.getCache();
83 
84     const char* start = (char*)glyphs;
85     const char* stop = (char*)(glyphs + len);
86     SkFixed xAdv = 0, yAdv = 0;
87 
88     // TODO(vandebo) This probably needs to take kerning into account.
89     while (start < stop) {
90         const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
91         xAdv += glyph.fAdvanceX;
92         yAdv += glyph.fAdvanceY;
93     };
94     if (width)
95         *width = SkFixedToScalar(xAdv);
96     if (paint.getTextAlign() == SkPaint::kLeft_Align)
97         return;
98 
99     SkScalar xAdj = SkFixedToScalar(xAdv);
100     SkScalar yAdj = SkFixedToScalar(yAdv);
101     if (paint.getTextAlign() == SkPaint::kCenter_Align) {
102         xAdj = SkScalarHalf(xAdj);
103         yAdj = SkScalarHalf(yAdj);
104     }
105     *x = *x - xAdj;
106     *y = *y - yAdj;
107 }
108 
set_text_transform(SkScalar x,SkScalar y,SkScalar textSkewX,SkWStream * content)109 static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
110                                SkWStream* content) {
111     // Flip the text about the x-axis to account for origin swap and include
112     // the passed parameters.
113     content->writeText("1 0 ");
114     SkPDFScalar::Append(0 - textSkewX, content);
115     content->writeText(" -1 ");
116     SkPDFScalar::Append(x, content);
117     content->writeText(" ");
118     SkPDFScalar::Append(y, content);
119     content->writeText(" Tm\n");
120 }
121 
122 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
123 // later being our representation of an object in the PDF file.
124 struct GraphicStateEntry {
125     GraphicStateEntry();
126 
127     // Compare the fields we care about when setting up a new content entry.
128     bool compareInitialState(const GraphicStateEntry& b);
129 
130     SkMatrix fMatrix;
131     // We can't do set operations on Paths, though PDF natively supports
132     // intersect.  If the clip stack does anything other than intersect,
133     // we have to fall back to the region.  Treat fClipStack as authoritative.
134     // See http://code.google.com/p/skia/issues/detail?id=221
135     SkClipStack fClipStack;
136     SkRegion fClipRegion;
137 
138     // When emitting the content entry, we will ensure the graphic state
139     // is set to these values first.
140     SkColor fColor;
141     SkScalar fTextScaleX;  // Zero means we don't care what the value is.
142     SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero.
143     int fShaderIndex;
144     int fGraphicStateIndex;
145 
146     // We may change the font (i.e. for Type1 support) within a
147     // ContentEntry.  This is the one currently in effect, or NULL if none.
148     SkPDFFont* fFont;
149     // In PDF, text size has no default value. It is only valid if fFont is
150     // not NULL.
151     SkScalar fTextSize;
152 };
153 
GraphicStateEntry()154 GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
155                                          fTextScaleX(SK_Scalar1),
156                                          fTextFill(SkPaint::kFill_Style),
157                                          fShaderIndex(-1),
158                                          fGraphicStateIndex(-1),
159                                          fFont(NULL),
160                                          fTextSize(SK_ScalarNaN) {
161     fMatrix.reset();
162 }
163 
compareInitialState(const GraphicStateEntry & b)164 bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) {
165     return fColor == b.fColor &&
166            fShaderIndex == b.fShaderIndex &&
167            fGraphicStateIndex == b.fGraphicStateIndex &&
168            fMatrix == b.fMatrix &&
169            fClipStack == b.fClipStack &&
170                (fTextScaleX == 0 ||
171                 b.fTextScaleX == 0 ||
172                 (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill));
173 }
174 
175 class GraphicStackState {
176 public:
GraphicStackState(const SkClipStack & existingClipStack,const SkRegion & existingClipRegion,SkWStream * contentStream)177     GraphicStackState(const SkClipStack& existingClipStack,
178                       const SkRegion& existingClipRegion,
179                       SkWStream* contentStream)
180             : fStackDepth(0),
181               fContentStream(contentStream) {
182         fEntries[0].fClipStack = existingClipStack;
183         fEntries[0].fClipRegion = existingClipRegion;
184     }
185 
186     void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
187                     const SkIPoint& translation);
188     void updateMatrix(const SkMatrix& matrix);
189     void updateDrawingState(const GraphicStateEntry& state);
190 
191     void drainStack();
192 
193 private:
194     void push();
195     void pop();
currentEntry()196     GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
197 
198     // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
199     static const int kMaxStackDepth = 12;
200     GraphicStateEntry fEntries[kMaxStackDepth + 1];
201     int fStackDepth;
202     SkWStream* fContentStream;
203 };
204 
drainStack()205 void GraphicStackState::drainStack() {
206     while (fStackDepth) {
207         pop();
208     }
209 }
210 
push()211 void GraphicStackState::push() {
212     SkASSERT(fStackDepth < kMaxStackDepth);
213     fContentStream->writeText("q\n");
214     fStackDepth++;
215     fEntries[fStackDepth] = fEntries[fStackDepth - 1];
216 }
217 
pop()218 void GraphicStackState::pop() {
219     SkASSERT(fStackDepth > 0);
220     fContentStream->writeText("Q\n");
221     fStackDepth--;
222 }
223 
224 // This function initializes iter to be an interator on the "stack" argument
225 // and then skips over the leading entries as specified in prefix.  It requires
226 // and asserts that "prefix" will be a prefix to "stack."
skip_clip_stack_prefix(const SkClipStack & prefix,const SkClipStack & stack,SkClipStack::B2FIter * iter)227 static void skip_clip_stack_prefix(const SkClipStack& prefix,
228                                    const SkClipStack& stack,
229                                    SkClipStack::B2FIter* iter) {
230     SkClipStack::B2FIter prefixIter(prefix);
231     iter->reset(stack);
232 
233     const SkClipStack::B2FIter::Clip* prefixEntry;
234     const SkClipStack::B2FIter::Clip* iterEntry;
235 
236     int count = 0;
237     for (prefixEntry = prefixIter.next(); prefixEntry;
238             prefixEntry = prefixIter.next(), count++) {
239         iterEntry = iter->next();
240         SkASSERT(iterEntry);
241         // Because of SkClipStack does internal intersection, the last clip
242         // entry may differ.
243         if(*prefixEntry != *iterEntry) {
244             SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op);
245             SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op);
246             SkASSERT((iterEntry->fRect == NULL) ==
247                     (prefixEntry->fRect == NULL));
248             SkASSERT((iterEntry->fPath == NULL) ==
249                     (prefixEntry->fPath == NULL));
250             // We need to back up the iterator by one but don't have that
251             // function, so reset and go forward by one less.
252             iter->reset(stack);
253             for (int i = 0; i < count; i++) {
254                 iter->next();
255             }
256             prefixEntry = prefixIter.next();
257             break;
258         }
259     }
260 
261     SkASSERT(prefixEntry == NULL);
262 }
263 
emit_clip(SkPath * clipPath,SkRect * clipRect,SkWStream * contentStream)264 static void emit_clip(SkPath* clipPath, SkRect* clipRect,
265                       SkWStream* contentStream) {
266     SkASSERT(clipPath || clipRect);
267 
268     SkPath::FillType clipFill;
269     if (clipPath) {
270         SkPDFUtils::EmitPath(*clipPath, contentStream);
271         clipFill = clipPath->getFillType();
272     } else if (clipRect) {
273         SkPDFUtils::AppendRectangle(*clipRect, contentStream);
274         clipFill = SkPath::kWinding_FillType;
275     }
276 
277     NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
278     NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
279     if (clipFill == SkPath::kEvenOdd_FillType) {
280         contentStream->writeText("W* n\n");
281     } else {
282         contentStream->writeText("W n\n");
283     }
284 }
285 
286 // TODO(vandebo) Take advantage of SkClipStack::getSaveCount(), the PDF
287 // graphic state stack, and the fact that we can know all the clips used
288 // on the page to optimize this.
updateClip(const SkClipStack & clipStack,const SkRegion & clipRegion,const SkIPoint & translation)289 void GraphicStackState::updateClip(const SkClipStack& clipStack,
290                                    const SkRegion& clipRegion,
291                                    const SkIPoint& translation) {
292     if (clipStack == currentEntry()->fClipStack) {
293         return;
294     }
295 
296     while (fStackDepth > 0) {
297         pop();
298         if (clipStack == currentEntry()->fClipStack) {
299             return;
300         }
301     }
302     push();
303 
304     // gsState->initialEntry()->fClipStack/Region specifies the clip that has
305     // already been applied.  (If this is a top level device, then it specifies
306     // a clip to the content area.  If this is a layer, then it specifies
307     // the clip in effect when the layer was created.)  There's no need to
308     // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
309     // initial clip on the parent layer.  (This means there's a bug if the user
310     // expands the clip and then uses any xfer mode that uses dst:
311     // http://code.google.com/p/skia/issues/detail?id=228 )
312     SkClipStack::B2FIter iter;
313     skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
314 
315     // If the clip stack does anything other than intersect or if it uses
316     // an inverse fill type, we have to fall back to the clip region.
317     bool needRegion = false;
318     const SkClipStack::B2FIter::Clip* clipEntry;
319     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
320         if (clipEntry->fOp != SkRegion::kIntersect_Op ||
321                 (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) {
322             needRegion = true;
323             break;
324         }
325     }
326 
327     if (needRegion) {
328         SkPath clipPath;
329         SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
330         emit_clip(&clipPath, NULL, fContentStream);
331     } else {
332         skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
333         SkMatrix transform;
334         transform.setTranslate(translation.fX, translation.fY);
335         const SkClipStack::B2FIter::Clip* clipEntry;
336         for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
337             SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op);
338             if (clipEntry->fRect) {
339                 SkRect translatedClip;
340                 transform.mapRect(&translatedClip, *clipEntry->fRect);
341                 emit_clip(NULL, &translatedClip, fContentStream);
342             } else if (clipEntry->fPath) {
343                 SkPath translatedPath;
344                 clipEntry->fPath->transform(transform, &translatedPath);
345                 emit_clip(&translatedPath, NULL, fContentStream);
346             } else {
347                 SkASSERT(false);
348             }
349         }
350     }
351     currentEntry()->fClipStack = clipStack;
352     currentEntry()->fClipRegion = clipRegion;
353 }
354 
updateMatrix(const SkMatrix & matrix)355 void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
356     if (matrix == currentEntry()->fMatrix) {
357         return;
358     }
359 
360     if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
361         SkASSERT(fStackDepth > 0);
362         SkASSERT(fEntries[fStackDepth].fClipStack ==
363                  fEntries[fStackDepth -1].fClipStack);
364         pop();
365 
366         SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
367     }
368     if (matrix.getType() == SkMatrix::kIdentity_Mask) {
369         return;
370     }
371 
372     push();
373     SkPDFUtils::AppendTransform(matrix, fContentStream);
374     currentEntry()->fMatrix = matrix;
375 }
376 
updateDrawingState(const GraphicStateEntry & state)377 void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
378     // PDF treats a shader as a color, so we only set one or the other.
379     if (state.fShaderIndex >= 0) {
380         if (state.fShaderIndex != currentEntry()->fShaderIndex) {
381             fContentStream->writeText("/Pattern CS /Pattern cs /P");
382             fContentStream->writeDecAsText(state.fShaderIndex);
383             fContentStream->writeText(" SCN /P");
384             fContentStream->writeDecAsText(state.fShaderIndex);
385             fContentStream->writeText(" scn\n");
386             currentEntry()->fShaderIndex = state.fShaderIndex;
387         }
388     } else {
389         if (state.fColor != currentEntry()->fColor ||
390                 currentEntry()->fShaderIndex >= 0) {
391             emit_pdf_color(state.fColor, fContentStream);
392             fContentStream->writeText("RG ");
393             emit_pdf_color(state.fColor, fContentStream);
394             fContentStream->writeText("rg\n");
395             currentEntry()->fColor = state.fColor;
396             currentEntry()->fShaderIndex = -1;
397         }
398     }
399 
400     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
401         SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
402         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
403     }
404 
405     if (state.fTextScaleX) {
406         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
407             SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
408                                             SkIntToScalar(100));
409             SkPDFScalar::Append(pdfScale, fContentStream);
410             fContentStream->writeText(" Tz\n");
411             currentEntry()->fTextScaleX = state.fTextScaleX;
412         }
413         if (state.fTextFill != currentEntry()->fTextFill) {
414             SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
415             SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
416                               enum_must_match_value);
417             SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
418                               enum_must_match_value);
419             fContentStream->writeDecAsText(state.fTextFill);
420             fContentStream->writeText(" Tr\n");
421             currentEntry()->fTextFill = state.fTextFill;
422         }
423     }
424 }
425 
426 struct ContentEntry {
427     GraphicStateEntry fState;
428     SkDynamicMemoryWStream fContent;
429     SkTScopedPtr<ContentEntry> fNext;
430 };
431 
432 // A helper class to automatically finish a ContentEntry at the end of a
433 // drawing method and maintain the state needed between set up and finish.
434 class ScopedContentEntry {
435 public:
ScopedContentEntry(SkPDFDevice * device,const SkDraw & draw,const SkPaint & paint,bool hasText=false)436     ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw,
437                        const SkPaint& paint, bool hasText = false)
438         : fDevice(device),
439           fContentEntry(NULL),
440           fXfermode(SkXfermode::kSrcOver_Mode) {
441         init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
442     }
ScopedContentEntry(SkPDFDevice * device,const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText=false)443     ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
444                        const SkRegion& clipRegion, const SkMatrix& matrix,
445                        const SkPaint& paint, bool hasText = false)
446         : fDevice(device),
447           fContentEntry(NULL),
448           fXfermode(SkXfermode::kSrcOver_Mode) {
449         init(clipStack, clipRegion, matrix, paint, hasText);
450     }
451 
~ScopedContentEntry()452     ~ScopedContentEntry() {
453         if (fContentEntry) {
454             fDevice->finishContentEntry(fXfermode, fDstFormXObject.get());
455         }
456     }
457 
entry()458     ContentEntry* entry() { return fContentEntry; }
459 private:
460     SkPDFDevice* fDevice;
461     ContentEntry* fContentEntry;
462     SkXfermode::Mode fXfermode;
463     SkRefPtr<SkPDFFormXObject> fDstFormXObject;
464 
init(const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText)465     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
466               const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
467         if (paint.getXfermode()) {
468             paint.getXfermode()->asMode(&fXfermode);
469         }
470         fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
471                                                    matrix, paint, hasText,
472                                                    &fDstFormXObject);
473     }
474 };
475 
476 ////////////////////////////////////////////////////////////////////////////////
477 
newDevice(SkCanvas * c,SkBitmap::Config config,int width,int height,bool isOpaque,bool isForLayer)478 SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas* c, SkBitmap::Config config,
479                                         int width, int height, bool isOpaque,
480                                         bool isForLayer) {
481     SkMatrix initialTransform;
482     initialTransform.reset();
483     SkISize size = SkISize::Make(width, height);
484     if (isForLayer) {
485         return SkNEW_ARGS(SkPDFDevice, (size, c->getTotalClipStack(),
486                                         c->getTotalClip()));
487     } else {
488         return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
489     }
490 }
491 
makeContentBitmap(const SkISize & contentSize,const SkMatrix * initialTransform)492 static inline SkBitmap makeContentBitmap(const SkISize& contentSize,
493                                          const SkMatrix* initialTransform) {
494     SkBitmap bitmap;
495     if (initialTransform) {
496         // Compute the size of the drawing area.
497         SkVector drawingSize;
498         SkMatrix inverse;
499         drawingSize.set(contentSize.fWidth, contentSize.fHeight);
500         initialTransform->invert(&inverse);
501         inverse.mapVectors(&drawingSize, 1);
502         SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
503         bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth),
504                          abs(size.fHeight));
505     } else {
506         bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth),
507                          abs(contentSize.fHeight));
508     }
509 
510     return bitmap;
511 }
512 
SkPDFDevice(const SkISize & pageSize,const SkISize & contentSize,const SkMatrix & initialTransform)513 SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
514                          const SkMatrix& initialTransform)
515     : SkDevice(NULL, makeContentBitmap(contentSize, &initialTransform), false),
516       fPageSize(pageSize),
517       fContentSize(contentSize),
518       fLastContentEntry(NULL) {
519     // Skia generally uses the top left as the origin but PDF natively has the
520     // origin at the bottom left. This matrix corrects for that.  But that only
521     // needs to be done once, we don't do it when layering.
522     fInitialTransform.setTranslate(0, pageSize.fHeight);
523     fInitialTransform.preScale(1, -1);
524     fInitialTransform.preConcat(initialTransform);
525 
526     SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height());
527     fExistingClipStack.clipDevRect(existingClip);
528     fExistingClipRegion.setRect(existingClip);
529 
530     this->init();
531 }
532 
SkPDFDevice(const SkISize & layerSize,const SkClipStack & existingClipStack,const SkRegion & existingClipRegion)533 SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
534                          const SkClipStack& existingClipStack,
535                          const SkRegion& existingClipRegion)
536     : SkDevice(NULL, makeContentBitmap(layerSize, NULL), false),
537       fPageSize(layerSize),
538       fContentSize(layerSize),
539       fExistingClipStack(existingClipStack),
540       fExistingClipRegion(existingClipRegion),
541       fLastContentEntry(NULL) {
542     fInitialTransform.reset();
543     this->init();
544 }
545 
~SkPDFDevice()546 SkPDFDevice::~SkPDFDevice() {
547     this->cleanUp();
548 }
549 
init()550 void SkPDFDevice::init() {
551     fResourceDict = NULL;
552     fContentEntries.reset();
553     fLastContentEntry = NULL;
554 }
555 
onNewDeviceFactory()556 SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() {
557     return SkNEW(SkPDFDeviceFactory);
558 }
559 
cleanUp()560 void SkPDFDevice::cleanUp() {
561     fGraphicStateResources.unrefAll();
562     fXObjectResources.unrefAll();
563     fFontResources.unrefAll();
564     fShaderResources.unrefAll();
565 }
566 
clear(SkColor color)567 void SkPDFDevice::clear(SkColor color) {
568     this->cleanUp();
569     this->init();
570 
571     SkPaint paint;
572     paint.setColor(color);
573     paint.setStyle(SkPaint::kFill_Style);
574     SkMatrix identity;
575     identity.reset();
576     ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
577                                identity, paint);
578     internalDrawPaint(paint, content.entry());
579 }
580 
drawPaint(const SkDraw & d,const SkPaint & paint)581 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
582     SkPaint newPaint = paint;
583     newPaint.setStyle(SkPaint::kFill_Style);
584     ScopedContentEntry content(this, d, newPaint);
585     internalDrawPaint(newPaint, content.entry());
586 }
587 
internalDrawPaint(const SkPaint & paint,ContentEntry * contentEntry)588 void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
589                                     ContentEntry* contentEntry) {
590     if (!contentEntry) {
591         return;
592     }
593     SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
594                                  SkIntToScalar(this->height()));
595     SkMatrix totalTransform = fInitialTransform;
596     totalTransform.preConcat(contentEntry->fState.fMatrix);
597     SkMatrix inverse;
598     inverse.reset();
599     totalTransform.invert(&inverse);
600     inverse.mapRect(&bbox);
601 
602     SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
603     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
604                           &contentEntry->fContent);
605 }
606 
drawPoints(const SkDraw & d,SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & passedPaint)607 void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
608                              size_t count, const SkPoint* points,
609                              const SkPaint& passedPaint) {
610     if (count == 0) {
611         return;
612     }
613 
614     // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
615     // We only use this when there's a path effect because of the overhead
616     // of multiple calls to setUpContentEntry it causes.
617     if (passedPaint.getPathEffect()) {
618         if (d.fClip->isEmpty()) {
619             return;
620         }
621         SkDraw pointDraw(d);
622         pointDraw.fDevice = this;
623         pointDraw.drawPoints(mode, count, points, passedPaint, true);
624         return;
625     }
626 
627     const SkPaint* paint = &passedPaint;
628     SkPaint modifiedPaint;
629 
630     if (mode == SkCanvas::kPoints_PointMode &&
631             paint->getStrokeCap() != SkPaint::kRound_Cap) {
632         modifiedPaint = *paint;
633         paint = &modifiedPaint;
634         if (paint->getStrokeWidth()) {
635             // PDF won't draw a single point with square/butt caps because the
636             // orientation is ambiguous.  Draw a rectangle instead.
637             modifiedPaint.setStyle(SkPaint::kFill_Style);
638             SkScalar strokeWidth = paint->getStrokeWidth();
639             SkScalar halfStroke = SkScalarHalf(strokeWidth);
640             for (size_t i = 0; i < count; i++) {
641                 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
642                 r.inset(-halfStroke, -halfStroke);
643                 drawRect(d, r, modifiedPaint);
644             }
645             return;
646         } else {
647             modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
648         }
649     }
650 
651     ScopedContentEntry content(this, d, *paint);
652     if (!content.entry()) {
653         return;
654     }
655 
656     switch (mode) {
657         case SkCanvas::kPolygon_PointMode:
658             SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
659                                &content.entry()->fContent);
660             for (size_t i = 1; i < count; i++) {
661                 SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
662                                        &content.entry()->fContent);
663             }
664             SkPDFUtils::StrokePath(&content.entry()->fContent);
665             break;
666         case SkCanvas::kLines_PointMode:
667             for (size_t i = 0; i < count/2; i++) {
668                 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
669                                    &content.entry()->fContent);
670                 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
671                                        points[i * 2 + 1].fY,
672                                        &content.entry()->fContent);
673                 SkPDFUtils::StrokePath(&content.entry()->fContent);
674             }
675             break;
676         case SkCanvas::kPoints_PointMode:
677             SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
678             for (size_t i = 0; i < count; i++) {
679                 SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
680                                    &content.entry()->fContent);
681                 SkPDFUtils::ClosePath(&content.entry()->fContent);
682                 SkPDFUtils::StrokePath(&content.entry()->fContent);
683             }
684             break;
685         default:
686             SkASSERT(false);
687     }
688 }
689 
drawRect(const SkDraw & d,const SkRect & r,const SkPaint & paint)690 void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
691                            const SkPaint& paint) {
692     if (paint.getPathEffect()) {
693         if (d.fClip->isEmpty()) {
694             return;
695         }
696         SkPath path;
697         path.addRect(r);
698         drawPath(d, path, paint, NULL, true);
699         return;
700     }
701 
702     ScopedContentEntry content(this, d, paint);
703     if (!content.entry()) {
704         return;
705     }
706     SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
707     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
708                           &content.entry()->fContent);
709 }
710 
drawPath(const SkDraw & d,const SkPath & origPath,const SkPaint & paint,const SkMatrix * prePathMatrix,bool pathIsMutable)711 void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
712                            const SkPaint& paint, const SkMatrix* prePathMatrix,
713                            bool pathIsMutable) {
714     SkPath modifiedPath;
715     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
716 
717     SkMatrix matrix = *d.fMatrix;
718     if (prePathMatrix) {
719         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
720             if (!pathIsMutable) {
721                 pathPtr = &modifiedPath;
722                 pathIsMutable = true;
723             }
724             origPath.transform(*prePathMatrix, pathPtr);
725         } else {
726             if (!matrix.preConcat(*prePathMatrix)) {
727                 return;
728             }
729         }
730     }
731 
732     if (paint.getPathEffect()) {
733         if (d.fClip->isEmpty()) {
734             return;
735         }
736         if (!pathIsMutable) {
737             pathPtr = &modifiedPath;
738             pathIsMutable = true;
739         }
740         bool fill = paint.getFillPath(origPath, pathPtr);
741 
742         SkPaint noEffectPaint(paint);
743         noEffectPaint.setPathEffect(NULL);
744         if (fill) {
745             noEffectPaint.setStyle(SkPaint::kFill_Style);
746         } else {
747             noEffectPaint.setStyle(SkPaint::kStroke_Style);
748             noEffectPaint.setStrokeWidth(0);
749         }
750         drawPath(d, *pathPtr, noEffectPaint, NULL, true);
751         return;
752     }
753 
754     ScopedContentEntry content(this, d, paint);
755     if (!content.entry()) {
756         return;
757     }
758     SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent);
759     SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
760                           &content.entry()->fContent);
761 }
762 
drawBitmap(const SkDraw & d,const SkBitmap & bitmap,const SkIRect * srcRect,const SkMatrix & matrix,const SkPaint & paint)763 void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
764                              const SkIRect* srcRect, const SkMatrix& matrix,
765                              const SkPaint& paint) {
766     if (d.fClip->isEmpty()) {
767         return;
768     }
769 
770     SkMatrix transform = matrix;
771     transform.postConcat(*d.fMatrix);
772     internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect,
773                        paint);
774 }
775 
drawSprite(const SkDraw & d,const SkBitmap & bitmap,int x,int y,const SkPaint & paint)776 void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
777                              int x, int y, const SkPaint& paint) {
778     if (d.fClip->isEmpty()) {
779         return;
780     }
781 
782     SkMatrix matrix;
783     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
784     internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint);
785 }
786 
drawText(const SkDraw & d,const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & paint)787 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
788                            SkScalar x, SkScalar y, const SkPaint& paint) {
789     SkPaint textPaint = calculate_text_paint(paint);
790     ScopedContentEntry content(this, d, textPaint, true);
791     if (!content.entry()) {
792         return;
793     }
794 
795     // We want the text in glyph id encoding and a writable buffer, so we end
796     // up making a copy either way.
797     size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
798     uint16_t* glyphIDs =
799         (uint16_t*)sk_malloc_flags(numGlyphs * 2,
800                                    SK_MALLOC_TEMP | SK_MALLOC_THROW);
801     SkAutoFree autoFreeGlyphIDs(glyphIDs);
802     if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
803         paint.textToGlyphs(text, len, glyphIDs);
804         textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
805     } else {
806         SkASSERT((len & 1) == 0);
807         SkASSERT(len / 2 == numGlyphs);
808         memcpy(glyphIDs, text, len);
809     }
810 
811     SkScalar width;
812     SkScalar* widthPtr = NULL;
813     if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
814         widthPtr = &width;
815 
816     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
817     align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y,
818                widthPtr);
819     content.entry()->fContent.writeText("BT\n");
820     set_text_transform(x, y, textPaint.getTextSkewX(),
821                        &content.entry()->fContent);
822     size_t consumedGlyphCount = 0;
823     while (numGlyphs > consumedGlyphCount) {
824         updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry());
825         SkPDFFont* font = content.entry()->fState.fFont;
826         size_t availableGlyphs =
827             font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
828                                           numGlyphs - consumedGlyphCount);
829         SkString encodedString =
830             SkPDFString::formatString(glyphIDs + consumedGlyphCount,
831                                       availableGlyphs, font->multiByteGlyphs());
832         content.entry()->fContent.writeText(encodedString.c_str());
833         consumedGlyphCount += availableGlyphs;
834         content.entry()->fContent.writeText(" Tj\n");
835     }
836     content.entry()->fContent.writeText("ET\n");
837 
838     // Draw underline and/or strikethrough if the paint has them.
839     // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
840     // because the raster versions don't.  Use paint instead of textPaint
841     // because we may have changed strokeWidth to do fakeBold text.
842     if (paint.isUnderlineText() || paint.isStrikeThruText()) {
843         SkScalar textSize = paint.getTextSize();
844         SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
845 
846         if (paint.isUnderlineText()) {
847             SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
848             SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
849             drawRect(d, r, paint);
850         }
851         if (paint.isStrikeThruText()) {
852             SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
853             SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
854             drawRect(d, r, paint);
855         }
856     }
857 }
858 
drawPosText(const SkDraw & d,const void * text,size_t len,const SkScalar pos[],SkScalar constY,int scalarsPerPos,const SkPaint & paint)859 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
860                               const SkScalar pos[], SkScalar constY,
861                               int scalarsPerPos, const SkPaint& paint) {
862     SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
863     SkPaint textPaint = calculate_text_paint(paint);
864     ScopedContentEntry content(this, d, textPaint, true);
865     if (!content.entry()) {
866         return;
867     }
868 
869     // Make sure we have a glyph id encoding.
870     SkAutoFree glyphStorage;
871     uint16_t* glyphIDs;
872     size_t numGlyphs;
873     if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
874         numGlyphs = paint.textToGlyphs(text, len, NULL);
875         glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
876                                               SK_MALLOC_TEMP | SK_MALLOC_THROW);
877         glyphStorage.set(glyphIDs);
878         paint.textToGlyphs(text, len, glyphIDs);
879         textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
880     } else {
881         SkASSERT((len & 1) == 0);
882         numGlyphs = len / 2;
883         glyphIDs = (uint16_t*)text;
884     }
885 
886     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
887     content.entry()->fContent.writeText("BT\n");
888     updateFont(textPaint, glyphIDs[0], content.entry());
889     for (size_t i = 0; i < numGlyphs; i++) {
890         SkPDFFont* font = content.entry()->fState.fFont;
891         uint16_t encodedValue = glyphIDs[i];
892         if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
893             updateFont(textPaint, glyphIDs[i], content.entry());
894             i--;
895             continue;
896         }
897         SkScalar x = pos[i * scalarsPerPos];
898         SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
899         align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
900         set_text_transform(x, y, textPaint.getTextSkewX(),
901                            &content.entry()->fContent);
902         SkString encodedString =
903             SkPDFString::formatString(&encodedValue, 1,
904                                       font->multiByteGlyphs());
905         content.entry()->fContent.writeText(encodedString.c_str());
906         content.entry()->fContent.writeText(" Tj\n");
907     }
908     content.entry()->fContent.writeText("ET\n");
909 }
910 
drawTextOnPath(const SkDraw & d,const void * text,size_t len,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)911 void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
912                                  const SkPath& path, const SkMatrix* matrix,
913                                  const SkPaint& paint) {
914     if (d.fClip->isEmpty()) {
915         return;
916     }
917     NOT_IMPLEMENTED("drawTextOnPath", true);
918 }
919 
drawVertices(const SkDraw & d,SkCanvas::VertexMode,int vertexCount,const SkPoint verts[],const SkPoint texs[],const SkColor colors[],SkXfermode * xmode,const uint16_t indices[],int indexCount,const SkPaint & paint)920 void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
921                                int vertexCount, const SkPoint verts[],
922                                const SkPoint texs[], const SkColor colors[],
923                                SkXfermode* xmode, const uint16_t indices[],
924                                int indexCount, const SkPaint& paint) {
925     if (d.fClip->isEmpty()) {
926         return;
927     }
928     NOT_IMPLEMENTED("drawVerticies", true);
929 }
930 
drawDevice(const SkDraw & d,SkDevice * device,int x,int y,const SkPaint & paint)931 void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
932                              const SkPaint& paint) {
933     if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
934         // If we somehow get a raster device, do what our parent would do.
935         SkDevice::drawDevice(d, device, x, y, paint);
936         return;
937     }
938 
939     // Assume that a vector capable device means that it's a PDF Device.
940     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
941     if (pdfDevice->isContentEmpty()) {
942         return;
943     }
944 
945     SkMatrix matrix;
946     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
947     ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
948     if (!content.entry()) {
949         return;
950     }
951 
952     SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
953     fXObjectResources.push(xobject);  // Transfer reference.
954     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
955                                 &content.entry()->fContent);
956 }
957 
getResourceDict()958 const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
959     if (fResourceDict.get() == NULL) {
960         fResourceDict = new SkPDFDict;
961         fResourceDict->unref();  // SkRefPtr and new both took a reference.
962 
963         if (fGraphicStateResources.count()) {
964             SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
965             extGState->unref();  // SkRefPtr and new both took a reference.
966             for (int i = 0; i < fGraphicStateResources.count(); i++) {
967                 SkString nameString("G");
968                 nameString.appendS32(i);
969                 extGState->insert(
970                         nameString.c_str(),
971                         new SkPDFObjRef(fGraphicStateResources[i]))->unref();
972             }
973             fResourceDict->insert("ExtGState", extGState.get());
974         }
975 
976         if (fXObjectResources.count()) {
977             SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
978             xObjects->unref();  // SkRefPtr and new both took a reference.
979             for (int i = 0; i < fXObjectResources.count(); i++) {
980                 SkString nameString("X");
981                 nameString.appendS32(i);
982                 xObjects->insert(
983                         nameString.c_str(),
984                         new SkPDFObjRef(fXObjectResources[i]))->unref();
985             }
986             fResourceDict->insert("XObject", xObjects.get());
987         }
988 
989         if (fFontResources.count()) {
990             SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
991             fonts->unref();  // SkRefPtr and new both took a reference.
992             for (int i = 0; i < fFontResources.count(); i++) {
993                 SkString nameString("F");
994                 nameString.appendS32(i);
995                 fonts->insert(nameString.c_str(),
996                               new SkPDFObjRef(fFontResources[i]))->unref();
997             }
998             fResourceDict->insert("Font", fonts.get());
999         }
1000 
1001         if (fShaderResources.count()) {
1002             SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
1003             patterns->unref();  // SkRefPtr and new both took a reference.
1004             for (int i = 0; i < fShaderResources.count(); i++) {
1005                 SkString nameString("P");
1006                 nameString.appendS32(i);
1007                 patterns->insert(nameString.c_str(),
1008                                  new SkPDFObjRef(fShaderResources[i]))->unref();
1009             }
1010             fResourceDict->insert("Pattern", patterns.get());
1011         }
1012 
1013         // For compatibility, add all proc sets (only used for output to PS
1014         // devices).
1015         const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
1016         SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
1017         procSets->unref();  // SkRefPtr and new both took a reference.
1018         procSets->reserve(SK_ARRAY_COUNT(procs));
1019         for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
1020             procSets->append(new SkPDFName(procs[i]))->unref();
1021         fResourceDict->insert("ProcSet", procSets.get());
1022     }
1023     return fResourceDict;
1024 }
1025 
getResources(SkTDArray<SkPDFObject * > * resourceList) const1026 void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
1027     resourceList->setReserve(resourceList->count() +
1028                              fGraphicStateResources.count() +
1029                              fXObjectResources.count() +
1030                              fFontResources.count() +
1031                              fShaderResources.count());
1032     for (int i = 0; i < fGraphicStateResources.count(); i++) {
1033         resourceList->push(fGraphicStateResources[i]);
1034         fGraphicStateResources[i]->ref();
1035         fGraphicStateResources[i]->getResources(resourceList);
1036     }
1037     for (int i = 0; i < fXObjectResources.count(); i++) {
1038         resourceList->push(fXObjectResources[i]);
1039         fXObjectResources[i]->ref();
1040         fXObjectResources[i]->getResources(resourceList);
1041     }
1042     for (int i = 0; i < fFontResources.count(); i++) {
1043         resourceList->push(fFontResources[i]);
1044         fFontResources[i]->ref();
1045         fFontResources[i]->getResources(resourceList);
1046     }
1047     for (int i = 0; i < fShaderResources.count(); i++) {
1048         resourceList->push(fShaderResources[i]);
1049         fShaderResources[i]->ref();
1050         fShaderResources[i]->getResources(resourceList);
1051     }
1052 }
1053 
getFontResources() const1054 const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
1055     return fFontResources;
1056 }
1057 
getMediaBox() const1058 SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
1059     SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
1060     zero->unref();  // SkRefPtr and new both took a reference.
1061 
1062     SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
1063     mediaBox->unref();  // SkRefPtr and new both took a reference.
1064     mediaBox->reserve(4);
1065     mediaBox->append(zero.get());
1066     mediaBox->append(zero.get());
1067     mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref();
1068     mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref();
1069     return mediaBox;
1070 }
1071 
content() const1072 SkStream* SkPDFDevice::content() const {
1073     SkDynamicMemoryWStream data;
1074     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
1075         SkPDFUtils::AppendTransform(fInitialTransform, &data);
1076     }
1077     // If the content area is the entire page, then we don't need to clip
1078     // the content area (PDF area clips to the page size).  Otherwise,
1079     // we have to clip to the content area; we've already applied the
1080     // initial transform, so just clip to the device size.
1081     if (fPageSize != fContentSize) {
1082         SkRect r = SkRect::MakeWH(this->width(), this->height());
1083         emit_clip(NULL, &r, &data);
1084     }
1085 
1086     GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, &data);
1087     for (ContentEntry* entry = fContentEntries.get();
1088             entry != NULL;
1089             entry = entry->fNext.get()) {
1090         SkIPoint translation = this->getOrigin();
1091         translation.negate();
1092         gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
1093                            translation);
1094         gsState.updateMatrix(entry->fState.fMatrix);
1095         gsState.updateDrawingState(entry->fState);
1096         data.write(entry->fContent.getStream(), entry->fContent.getOffset());
1097     }
1098     gsState.drainStack();
1099 
1100     SkMemoryStream* result = new SkMemoryStream;
1101     result->setMemoryOwned(data.detach(), data.getOffset());
1102     return result;
1103 }
1104 
createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject> * xobject)1105 void SkPDFDevice::createFormXObjectFromDevice(
1106         SkRefPtr<SkPDFFormXObject>* xobject) {
1107     *xobject = new SkPDFFormXObject(this);
1108     (*xobject)->unref();  // SkRefPtr and new both took a reference.
1109     cleanUp();  // Reset this device to have no content.
1110     init();
1111 }
1112 
clearClipFromContent(const SkClipStack * clipStack,const SkRegion & clipRegion)1113 void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
1114                                        const SkRegion& clipRegion) {
1115     if (clipRegion.isEmpty() || isContentEmpty()) {
1116         return;
1117     }
1118     SkRefPtr<SkPDFFormXObject> curContent;
1119     createFormXObjectFromDevice(&curContent);
1120 
1121     // Redraw what we already had, but with the clip as a mask.
1122     drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true);
1123 }
1124 
drawFormXObjectWithClip(SkPDFFormXObject * xobject,const SkClipStack * clipStack,const SkRegion & clipRegion,bool invertClip)1125 void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
1126                                           const SkClipStack* clipStack,
1127                                           const SkRegion& clipRegion,
1128                                           bool invertClip) {
1129     if (clipRegion.isEmpty() && !invertClip) {
1130         return;
1131     }
1132 
1133     // Create the mask.
1134     SkMatrix identity;
1135     identity.reset();
1136     SkDraw draw;
1137     draw.fMatrix = &identity;
1138     draw.fClip = &clipRegion;
1139     draw.fClipStack = clipStack;
1140     SkPaint stockPaint;
1141     this->drawPaint(draw, stockPaint);
1142     SkRefPtr<SkPDFFormXObject> maskFormXObject;
1143     createFormXObjectFromDevice(&maskFormXObject);
1144     SkRefPtr<SkPDFGraphicState> sMaskGS =
1145         SkPDFGraphicState::getSMaskGraphicState(maskFormXObject.get(),
1146                                                 invertClip);
1147     sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
1148 
1149     // Draw the xobject with the clip as a mask.
1150     ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
1151                                  identity, stockPaint);
1152     if (!content.entry()) {
1153         return;
1154     }
1155     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1156                                   &content.entry()->fContent);
1157     SkPDFUtils::DrawFormXObject(fXObjectResources.count(),
1158                                 &content.entry()->fContent);
1159     fXObjectResources.push(xobject);
1160     xobject->ref();
1161 
1162     sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
1163     sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
1164     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1165                                   &content.entry()->fContent);
1166 }
1167 
setUpContentEntry(const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText,SkRefPtr<SkPDFFormXObject> * dst)1168 ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
1169                                              const SkRegion& clipRegion,
1170                                              const SkMatrix& matrix,
1171                                              const SkPaint& paint,
1172                                              bool hasText,
1173                                              SkRefPtr<SkPDFFormXObject>* dst) {
1174     if (clipRegion.isEmpty()) {
1175         return NULL;
1176     }
1177 
1178     // The clip stack can come from an SkDraw where it is technically optional.
1179     SkClipStack synthesizedClipStack;
1180     if (clipStack == NULL) {
1181         if (clipRegion == fExistingClipRegion) {
1182             clipStack = &fExistingClipStack;
1183         } else {
1184             // GraphicStackState::updateClip expects the clip stack to have
1185             // fExistingClip as a prefix, so start there, then set the clip
1186             // to the passed region.
1187             synthesizedClipStack = fExistingClipStack;
1188             SkPath clipPath;
1189             clipRegion.getBoundaryPath(&clipPath);
1190             synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op);
1191             clipStack = &synthesizedClipStack;
1192         }
1193     }
1194 
1195     SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
1196     if (paint.getXfermode()) {
1197         paint.getXfermode()->asMode(&xfermode);
1198     }
1199 
1200     if (xfermode == SkXfermode::kClear_Mode ||
1201             xfermode == SkXfermode::kSrc_Mode) {
1202         this->clearClipFromContent(clipStack, clipRegion);
1203     } else if (xfermode == SkXfermode::kSrcIn_Mode ||
1204                xfermode == SkXfermode::kDstIn_Mode ||
1205                xfermode == SkXfermode::kSrcOut_Mode ||
1206                xfermode == SkXfermode::kDstOut_Mode) {
1207         // For the following modes, we use both source and destination, but
1208         // we use one as a smask for the other, so we have to make form xobjects
1209         // out of both of them: SrcIn, DstIn, SrcOut, DstOut.
1210         if (isContentEmpty()) {
1211             return NULL;
1212         } else {
1213             createFormXObjectFromDevice(dst);
1214         }
1215     }
1216     // TODO(vandebo) Figure out how/if we can handle the following modes:
1217     // SrcAtop, DestAtop, Xor, Plus.
1218 
1219     // These xfer modes don't draw source at all.
1220     if (xfermode == SkXfermode::kClear_Mode ||
1221             xfermode == SkXfermode::kDst_Mode) {
1222         return NULL;
1223     }
1224 
1225     ContentEntry* entry;
1226     SkTScopedPtr<ContentEntry> newEntry;
1227     if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) {
1228         entry = fLastContentEntry;
1229     } else {
1230         newEntry.reset(new ContentEntry);
1231         entry = newEntry.get();
1232     }
1233 
1234     populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
1235                                        hasText, &entry->fState);
1236     if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
1237             entry->fState.compareInitialState(fLastContentEntry->fState)) {
1238         return fLastContentEntry;
1239     }
1240 
1241     if (!fLastContentEntry) {
1242         fContentEntries.reset(entry);
1243         fLastContentEntry = entry;
1244     } else if (xfermode == SkXfermode::kDstOver_Mode) {
1245         entry->fNext.reset(fContentEntries.release());
1246         fContentEntries.reset(entry);
1247     } else {
1248         fLastContentEntry->fNext.reset(entry);
1249         fLastContentEntry = entry;
1250     }
1251     newEntry.release();
1252     return entry;
1253 }
1254 
finishContentEntry(const SkXfermode::Mode xfermode,SkPDFFormXObject * dst)1255 void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
1256                                      SkPDFFormXObject* dst) {
1257     if (xfermode != SkXfermode::kSrcIn_Mode &&
1258             xfermode != SkXfermode::kDstIn_Mode &&
1259             xfermode != SkXfermode::kSrcOut_Mode &&
1260             xfermode != SkXfermode::kDstOut_Mode) {
1261         SkASSERT(!dst);
1262         return;
1263     }
1264     SkASSERT(dst);
1265     SkASSERT(!fContentEntries->fNext.get());
1266 
1267     // We have to make a copy of these here because changing the current
1268     // content into a form xobject will destroy them.
1269     SkClipStack clipStack = fContentEntries->fState.fClipStack;
1270     SkRegion clipRegion = fContentEntries->fState.fClipRegion;
1271 
1272     SkRefPtr<SkPDFFormXObject> srcFormXObject;
1273     if (!isContentEmpty()) {
1274         createFormXObjectFromDevice(&srcFormXObject);
1275     }
1276 
1277     drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
1278 
1279     // We've redrawn dst minus the clip area, if there's no src, we're done.
1280     if (!srcFormXObject.get()) {
1281         return;
1282     }
1283 
1284     SkMatrix identity;
1285     identity.reset();
1286     SkPaint stockPaint;
1287     ScopedContentEntry inClipContentEntry(this, &fExistingClipStack,
1288                                           fExistingClipRegion, identity,
1289                                           stockPaint);
1290     if (!inClipContentEntry.entry()) {
1291         return;
1292     }
1293 
1294     SkRefPtr<SkPDFGraphicState> sMaskGS;
1295     if (xfermode == SkXfermode::kSrcIn_Mode ||
1296             xfermode == SkXfermode::kSrcOut_Mode) {
1297         sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
1298                 dst, xfermode == SkXfermode::kSrcOut_Mode);
1299         fXObjectResources.push(srcFormXObject.get());
1300         srcFormXObject->ref();
1301     } else {
1302         sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
1303                 srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
1304         // dst already added to fXObjectResources in drawFormXObjectWithClip.
1305     }
1306     sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
1307     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1308                                   &inClipContentEntry.entry()->fContent);
1309 
1310     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
1311                                 &inClipContentEntry.entry()->fContent);
1312 
1313     sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
1314     sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
1315     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1316                                   &inClipContentEntry.entry()->fContent);
1317 }
1318 
isContentEmpty()1319 bool SkPDFDevice::isContentEmpty() {
1320     if (!fContentEntries.get() || fContentEntries->fContent.getOffset() == 0) {
1321         SkASSERT(!fContentEntries.get() || !fContentEntries->fNext.get());
1322         return true;
1323     }
1324     return false;
1325 }
1326 
1327 
populateGraphicStateEntryFromPaint(const SkMatrix & matrix,const SkClipStack & clipStack,const SkRegion & clipRegion,const SkPaint & paint,bool hasText,GraphicStateEntry * entry)1328 void SkPDFDevice::populateGraphicStateEntryFromPaint(
1329         const SkMatrix& matrix,
1330         const SkClipStack& clipStack,
1331         const SkRegion& clipRegion,
1332         const SkPaint& paint,
1333         bool hasText,
1334         GraphicStateEntry* entry) {
1335     SkASSERT(paint.getPathEffect() == NULL);
1336 
1337     NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
1338     NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
1339 
1340     entry->fMatrix = matrix;
1341     entry->fClipStack = clipStack;
1342     entry->fClipRegion = clipRegion;
1343 
1344     // PDF treats a shader as a color, so we only set one or the other.
1345     SkRefPtr<SkPDFShader> pdfShader;
1346     const SkShader* shader = paint.getShader();
1347     SkColor color = paint.getColor();
1348     if (shader) {
1349         // PDF positions patterns relative to the initial transform, so
1350         // we need to apply the current transform to the shader parameters.
1351         SkMatrix transform = matrix;
1352         transform.postConcat(fInitialTransform);
1353 
1354         // PDF doesn't support kClamp_TileMode, so we simulate it by making
1355         // a pattern the size of the current clip.
1356         SkIRect bounds = clipRegion.getBounds();
1357         pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
1358         SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
1359 
1360         // A color shader is treated as an invalid shader so we don't have
1361         // to set a shader just for a color.
1362         if (pdfShader.get() == NULL) {
1363             entry->fColor = 0;
1364             color = 0;
1365 
1366             // Check for a color shader.
1367             SkShader::GradientInfo gradientInfo;
1368             SkColor gradientColor;
1369             gradientInfo.fColors = &gradientColor;
1370             gradientInfo.fColorOffsets = NULL;
1371             gradientInfo.fColorCount = 1;
1372             if (shader->asAGradient(&gradientInfo) ==
1373                     SkShader::kColor_GradientType) {
1374                 entry->fColor = SkColorSetA(gradientColor, 0xFF);
1375                 color = gradientColor;
1376             }
1377         }
1378     }
1379 
1380     if (pdfShader) {
1381         // pdfShader has been canonicalized so we can directly compare
1382         // pointers.
1383         int resourceIndex = fShaderResources.find(pdfShader.get());
1384         if (resourceIndex < 0) {
1385             resourceIndex = fShaderResources.count();
1386             fShaderResources.push(pdfShader.get());
1387             pdfShader->ref();
1388         }
1389         entry->fShaderIndex = resourceIndex;
1390     } else {
1391         entry->fShaderIndex = -1;
1392         entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
1393         color = paint.getColor();
1394     }
1395 
1396     SkRefPtr<SkPDFGraphicState> newGraphicState;
1397     if (color == paint.getColor()) {
1398         newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(paint);
1399     } else {
1400         SkPaint newPaint = paint;
1401         newPaint.setColor(color);
1402         newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint);
1403     }
1404     newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
1405     int resourceIndex = addGraphicStateResource(newGraphicState.get());
1406     entry->fGraphicStateIndex = resourceIndex;
1407 
1408     if (hasText) {
1409         entry->fTextScaleX = paint.getTextScaleX();
1410         entry->fTextFill = paint.getStyle();
1411     } else {
1412         entry->fTextScaleX = 0;
1413     }
1414 }
1415 
addGraphicStateResource(SkPDFGraphicState * gs)1416 int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
1417     // Assumes that gs has been canonicalized (so we can directly compare
1418     // pointers).
1419     int result = fGraphicStateResources.find(gs);
1420     if (result < 0) {
1421         result = fGraphicStateResources.count();
1422         fGraphicStateResources.push(gs);
1423         gs->ref();
1424     }
1425     return result;
1426 }
1427 
updateFont(const SkPaint & paint,uint16_t glyphID,ContentEntry * contentEntry)1428 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
1429                              ContentEntry* contentEntry) {
1430     SkTypeface* typeface = paint.getTypeface();
1431     if (contentEntry->fState.fFont == NULL ||
1432             contentEntry->fState.fTextSize != paint.getTextSize() ||
1433             !contentEntry->fState.fFont->hasGlyph(glyphID)) {
1434         int fontIndex = getFontResourceIndex(typeface, glyphID);
1435         contentEntry->fContent.writeText("/F");
1436         contentEntry->fContent.writeDecAsText(fontIndex);
1437         contentEntry->fContent.writeText(" ");
1438         SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent);
1439         contentEntry->fContent.writeText(" Tf\n");
1440         contentEntry->fState.fFont = fFontResources[fontIndex];
1441     }
1442 }
1443 
getFontResourceIndex(SkTypeface * typeface,uint16_t glyphID)1444 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
1445     SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
1446     newFont->unref();  // getFontResource and SkRefPtr both took a ref.
1447     int resourceIndex = fFontResources.find(newFont.get());
1448     if (resourceIndex < 0) {
1449         resourceIndex = fFontResources.count();
1450         fFontResources.push(newFont.get());
1451         newFont->ref();
1452     }
1453     return resourceIndex;
1454 }
1455 
internalDrawBitmap(const SkMatrix & matrix,const SkClipStack * clipStack,const SkRegion & clipRegion,const SkBitmap & bitmap,const SkIRect * srcRect,const SkPaint & paint)1456 void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
1457                                      const SkClipStack* clipStack,
1458                                      const SkRegion& clipRegion,
1459                                      const SkBitmap& bitmap,
1460                                      const SkIRect* srcRect,
1461                                      const SkPaint& paint) {
1462     SkMatrix scaled;
1463     // Adjust for origin flip.
1464     scaled.setScale(1, -1);
1465     scaled.postTranslate(0, 1);
1466     // Scale the image up from 1x1 to WxH.
1467     SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1468     scaled.postScale(SkIntToScalar(subset.width()),
1469                      SkIntToScalar(subset.height()));
1470     scaled.postConcat(matrix);
1471     ScopedContentEntry content(this, clipStack, clipRegion, scaled, paint);
1472     if (!content.entry()) {
1473         return;
1474     }
1475 
1476     if (srcRect && !subset.intersect(*srcRect)) {
1477         return;
1478     }
1479 
1480     SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
1481     if (!image) {
1482         return;
1483     }
1484 
1485     fXObjectResources.push(image);  // Transfer reference.
1486     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
1487                                 &content.entry()->fContent);
1488 }
1489