1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkPDFDevice.h"
9 
10 #include "SkAnnotation.h"
11 #include "SkColor.h"
12 #include "SkColorFilter.h"
13 #include "SkClipStack.h"
14 #include "SkData.h"
15 #include "SkDraw.h"
16 #include "SkGlyphCache.h"
17 #include "SkPaint.h"
18 #include "SkPath.h"
19 #include "SkPathOps.h"
20 #include "SkPDFBitmap.h"
21 #include "SkPDFCanon.h"
22 #include "SkPDFFont.h"
23 #include "SkPDFFormXObject.h"
24 #include "SkPDFGraphicState.h"
25 #include "SkPDFResourceDict.h"
26 #include "SkPDFShader.h"
27 #include "SkPDFStream.h"
28 #include "SkPDFTypes.h"
29 #include "SkPDFUtils.h"
30 #include "SkRasterClip.h"
31 #include "SkRect.h"
32 #include "SkRRect.h"
33 #include "SkString.h"
34 #include "SkSurface.h"
35 #include "SkTextFormatParams.h"
36 #include "SkTemplates.h"
37 #include "SkTypefacePriv.h"
38 #include "SkXfermodeInterpretation.h"
39 
40 #define DPI_FOR_RASTER_SCALE_ONE 72
41 
42 // Utility functions
43 
44 // If the paint will definitely draw opaquely, replace kSrc_Mode with
45 // kSrcOver_Mode.  http://crbug.com/473572
replace_srcmode_on_opaque_paint(SkPaint * paint)46 static void replace_srcmode_on_opaque_paint(SkPaint* paint) {
47     if (kSrcOver_SkXfermodeInterpretation
48         == SkInterpretXfermode(*paint, false)) {
49         paint->setXfermode(nullptr);
50     }
51 }
52 
emit_pdf_color(SkColor color,SkWStream * result)53 static void emit_pdf_color(SkColor color, SkWStream* result) {
54     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
55     SkScalar colorScale = SkScalarInvert(0xFF);
56     SkPDFUtils::AppendScalar(SkColorGetR(color) * colorScale, result);
57     result->writeText(" ");
58     SkPDFUtils::AppendScalar(SkColorGetG(color) * colorScale, result);
59     result->writeText(" ");
60     SkPDFUtils::AppendScalar(SkColorGetB(color) * colorScale, result);
61     result->writeText(" ");
62 }
63 
calculate_text_paint(const SkPaint & paint)64 static SkPaint calculate_text_paint(const SkPaint& paint) {
65     SkPaint result = paint;
66     if (result.isFakeBoldText()) {
67         SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
68                                                     kStdFakeBoldInterpKeys,
69                                                     kStdFakeBoldInterpValues,
70                                                     kStdFakeBoldInterpLength);
71         SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
72         if (result.getStyle() == SkPaint::kFill_Style) {
73             result.setStyle(SkPaint::kStrokeAndFill_Style);
74         } else {
75             width += result.getStrokeWidth();
76         }
77         result.setStrokeWidth(width);
78     }
79     return result;
80 }
81 
82 // Stolen from measure_text in SkDraw.cpp and then tweaked.
align_text(SkPaint::GlyphCacheProc glyphCacheProc,const SkPaint & paint,const uint16_t * glyphs,size_t len,SkScalar * x,SkScalar * y)83 static void align_text(SkPaint::GlyphCacheProc glyphCacheProc, const SkPaint& paint,
84                        const uint16_t* glyphs, size_t len,
85                        SkScalar* x, SkScalar* y) {
86     if (paint.getTextAlign() == SkPaint::kLeft_Align) {
87         return;
88     }
89 
90     SkMatrix ident;
91     ident.reset();
92     SkAutoGlyphCache autoCache(paint, nullptr, &ident);
93     SkGlyphCache* cache = autoCache.getCache();
94 
95     const char* start = reinterpret_cast<const char*>(glyphs);
96     const char* stop = reinterpret_cast<const char*>(glyphs + len);
97     SkFixed xAdv = 0, yAdv = 0;
98 
99     // TODO(vandebo): This probably needs to take kerning into account.
100     while (start < stop) {
101         const SkGlyph& glyph = glyphCacheProc(cache, &start);
102         xAdv += glyph.fAdvanceX;
103         yAdv += glyph.fAdvanceY;
104     };
105     if (paint.getTextAlign() == SkPaint::kLeft_Align) {
106         return;
107     }
108 
109     SkScalar xAdj = SkFixedToScalar(xAdv);
110     SkScalar yAdj = SkFixedToScalar(yAdv);
111     if (paint.getTextAlign() == SkPaint::kCenter_Align) {
112         xAdj = SkScalarHalf(xAdj);
113         yAdj = SkScalarHalf(yAdj);
114     }
115     *x = *x - xAdj;
116     *y = *y - yAdj;
117 }
118 
max_glyphid_for_typeface(SkTypeface * typeface)119 static int max_glyphid_for_typeface(SkTypeface* typeface) {
120     SkAutoResolveDefaultTypeface autoResolve(typeface);
121     typeface = autoResolve.get();
122     return typeface->countGlyphs() - 1;
123 }
124 
125 typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage;
126 
force_glyph_encoding(const SkPaint & paint,const void * text,size_t len,SkGlyphStorage * storage,const uint16_t ** glyphIDs)127 static int force_glyph_encoding(const SkPaint& paint, const void* text,
128                                 size_t len, SkGlyphStorage* storage,
129                                 const uint16_t** glyphIDs) {
130     // Make sure we have a glyph id encoding.
131     if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
132         int numGlyphs = paint.textToGlyphs(text, len, nullptr);
133         storage->reset(numGlyphs);
134         paint.textToGlyphs(text, len, storage->get());
135         *glyphIDs = storage->get();
136         return numGlyphs;
137     }
138 
139     // For user supplied glyph ids we need to validate them.
140     SkASSERT((len & 1) == 0);
141     int numGlyphs = SkToInt(len / 2);
142     const uint16_t* input = static_cast<const uint16_t*>(text);
143 
144     int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface());
145     int validated;
146     for (validated = 0; validated < numGlyphs; ++validated) {
147         if (input[validated] > maxGlyphID) {
148             break;
149         }
150     }
151     if (validated >= numGlyphs) {
152         *glyphIDs = static_cast<const uint16_t*>(text);
153         return numGlyphs;
154     }
155 
156     // Silently drop anything out of range.
157     storage->reset(numGlyphs);
158     if (validated > 0) {
159         memcpy(storage->get(), input, validated * sizeof(uint16_t));
160     }
161 
162     for (int i = validated; i < numGlyphs; ++i) {
163         storage->get()[i] = input[i];
164         if (input[i] > maxGlyphID) {
165             storage->get()[i] = 0;
166         }
167     }
168     *glyphIDs = storage->get();
169     return numGlyphs;
170 }
171 
set_text_transform(SkScalar x,SkScalar y,SkScalar textSkewX,SkWStream * content)172 static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
173                                SkWStream* content) {
174     // Flip the text about the x-axis to account for origin swap and include
175     // the passed parameters.
176     content->writeText("1 0 ");
177     SkPDFUtils::AppendScalar(0 - textSkewX, content);
178     content->writeText(" -1 ");
179     SkPDFUtils::AppendScalar(x, content);
180     content->writeText(" ");
181     SkPDFUtils::AppendScalar(y, content);
182     content->writeText(" Tm\n");
183 }
184 
185 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
186 // later being our representation of an object in the PDF file.
187 struct GraphicStateEntry {
188     GraphicStateEntry();
189 
190     // Compare the fields we care about when setting up a new content entry.
191     bool compareInitialState(const GraphicStateEntry& b);
192 
193     SkMatrix fMatrix;
194     // We can't do set operations on Paths, though PDF natively supports
195     // intersect.  If the clip stack does anything other than intersect,
196     // we have to fall back to the region.  Treat fClipStack as authoritative.
197     // See http://code.google.com/p/skia/issues/detail?id=221
198     SkClipStack fClipStack;
199     SkRegion fClipRegion;
200 
201     // When emitting the content entry, we will ensure the graphic state
202     // is set to these values first.
203     SkColor fColor;
204     SkScalar fTextScaleX;  // Zero means we don't care what the value is.
205     SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero.
206     int fShaderIndex;
207     int fGraphicStateIndex;
208 
209     // We may change the font (i.e. for Type1 support) within a
210     // ContentEntry.  This is the one currently in effect, or nullptr if none.
211     SkPDFFont* fFont;
212     // In PDF, text size has no default value. It is only valid if fFont is
213     // not nullptr.
214     SkScalar fTextSize;
215 };
216 
GraphicStateEntry()217 GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
218                                          fTextScaleX(SK_Scalar1),
219                                          fTextFill(SkPaint::kFill_Style),
220                                          fShaderIndex(-1),
221                                          fGraphicStateIndex(-1),
222                                          fFont(nullptr),
223                                          fTextSize(SK_ScalarNaN) {
224     fMatrix.reset();
225 }
226 
compareInitialState(const GraphicStateEntry & cur)227 bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) {
228     return fColor == cur.fColor &&
229            fShaderIndex == cur.fShaderIndex &&
230            fGraphicStateIndex == cur.fGraphicStateIndex &&
231            fMatrix == cur.fMatrix &&
232            fClipStack == cur.fClipStack &&
233            (fTextScaleX == 0 ||
234                (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill));
235 }
236 
237 class GraphicStackState {
238 public:
GraphicStackState(const SkClipStack & existingClipStack,const SkRegion & existingClipRegion,SkWStream * contentStream)239     GraphicStackState(const SkClipStack& existingClipStack,
240                       const SkRegion& existingClipRegion,
241                       SkWStream* contentStream)
242             : fStackDepth(0),
243               fContentStream(contentStream) {
244         fEntries[0].fClipStack = existingClipStack;
245         fEntries[0].fClipRegion = existingClipRegion;
246     }
247 
248     void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
249                     const SkPoint& translation);
250     void updateMatrix(const SkMatrix& matrix);
251     void updateDrawingState(const GraphicStateEntry& state);
252 
253     void drainStack();
254 
255 private:
256     void push();
257     void pop();
currentEntry()258     GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
259 
260     // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
261     static const int kMaxStackDepth = 12;
262     GraphicStateEntry fEntries[kMaxStackDepth + 1];
263     int fStackDepth;
264     SkWStream* fContentStream;
265 };
266 
drainStack()267 void GraphicStackState::drainStack() {
268     while (fStackDepth) {
269         pop();
270     }
271 }
272 
push()273 void GraphicStackState::push() {
274     SkASSERT(fStackDepth < kMaxStackDepth);
275     fContentStream->writeText("q\n");
276     fStackDepth++;
277     fEntries[fStackDepth] = fEntries[fStackDepth - 1];
278 }
279 
pop()280 void GraphicStackState::pop() {
281     SkASSERT(fStackDepth > 0);
282     fContentStream->writeText("Q\n");
283     fStackDepth--;
284 }
285 
286 // This function initializes iter to be an iterator on the "stack" argument
287 // and then skips over the leading entries as specified in prefix.  It requires
288 // and asserts that "prefix" will be a prefix to "stack."
skip_clip_stack_prefix(const SkClipStack & prefix,const SkClipStack & stack,SkClipStack::Iter * iter)289 static void skip_clip_stack_prefix(const SkClipStack& prefix,
290                                    const SkClipStack& stack,
291                                    SkClipStack::Iter* iter) {
292     SkClipStack::B2TIter prefixIter(prefix);
293     iter->reset(stack, SkClipStack::Iter::kBottom_IterStart);
294 
295     const SkClipStack::Element* prefixEntry;
296     const SkClipStack::Element* iterEntry;
297 
298     for (prefixEntry = prefixIter.next(); prefixEntry;
299             prefixEntry = prefixIter.next()) {
300         iterEntry = iter->next();
301         SkASSERT(iterEntry);
302         // Because of SkClipStack does internal intersection, the last clip
303         // entry may differ.
304         if (*prefixEntry != *iterEntry) {
305             SkASSERT(prefixEntry->getOp() == SkRegion::kIntersect_Op);
306             SkASSERT(iterEntry->getOp() == SkRegion::kIntersect_Op);
307             SkASSERT(iterEntry->getType() == prefixEntry->getType());
308             // back up the iterator by one
309             iter->prev();
310             prefixEntry = prefixIter.next();
311             break;
312         }
313     }
314 
315     SkASSERT(prefixEntry == nullptr);
316 }
317 
emit_clip(SkPath * clipPath,SkRect * clipRect,SkWStream * contentStream)318 static void emit_clip(SkPath* clipPath, SkRect* clipRect,
319                       SkWStream* contentStream) {
320     SkASSERT(clipPath || clipRect);
321 
322     SkPath::FillType clipFill;
323     if (clipPath) {
324         SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream);
325         clipFill = clipPath->getFillType();
326     } else {
327         SkPDFUtils::AppendRectangle(*clipRect, contentStream);
328         clipFill = SkPath::kWinding_FillType;
329     }
330 
331     NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
332     NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
333     if (clipFill == SkPath::kEvenOdd_FillType) {
334         contentStream->writeText("W* n\n");
335     } else {
336         contentStream->writeText("W n\n");
337     }
338 }
339 
340 /* Calculate an inverted path's equivalent non-inverted path, given the
341  * canvas bounds.
342  * outPath may alias with invPath (since this is supported by PathOps).
343  */
calculate_inverse_path(const SkRect & bounds,const SkPath & invPath,SkPath * outPath)344 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
345                                    SkPath* outPath) {
346     SkASSERT(invPath.isInverseFillType());
347 
348     SkPath clipPath;
349     clipPath.addRect(bounds);
350 
351     return Op(clipPath, invPath, kIntersect_SkPathOp, outPath);
352 }
353 
354 #ifdef SK_PDF_USE_PATHOPS_CLIPPING
355 // Sanity check the numerical values of the SkRegion ops and PathOps ops
356 // enums so region_op_to_pathops_op can do a straight passthrough cast.
357 // If these are failing, it may be necessary to make region_op_to_pathops_op
358 // do more.
359 static_assert(SkRegion::kDifference_Op == (int)kDifference_SkPathOp, "region_pathop_mismatch");
360 static_assert(SkRegion::kIntersect_Op == (int)kIntersect_SkPathOp, "region_pathop_mismatch");
361 static_assert(SkRegion::kUnion_Op == (int)kUnion_SkPathOp, "region_pathop_mismatch");
362 static_assert(SkRegion::kXOR_Op == (int)kXOR_SkPathOp, "region_pathop_mismatch");
363 static_assert(SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkPathOp,
364               "region_pathop_mismatch");
365 
region_op_to_pathops_op(SkRegion::Op op)366 static SkPathOp region_op_to_pathops_op(SkRegion::Op op) {
367     SkASSERT(op >= 0);
368     SkASSERT(op <= SkRegion::kReverseDifference_Op);
369     return (SkPathOp)op;
370 }
371 
372 /* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
373  * Returns true if successful, or false if not successful.
374  * If successful, the resulting clip is stored in outClipPath.
375  * If not successful, outClipPath is undefined, and a fallback method
376  * should be used.
377  */
get_clip_stack_path(const SkMatrix & transform,const SkClipStack & clipStack,const SkRegion & clipRegion,SkPath * outClipPath)378 static bool get_clip_stack_path(const SkMatrix& transform,
379                                 const SkClipStack& clipStack,
380                                 const SkRegion& clipRegion,
381                                 SkPath* outClipPath) {
382     outClipPath->reset();
383     outClipPath->setFillType(SkPath::kInverseWinding_FillType);
384 
385     const SkClipStack::Element* clipEntry;
386     SkClipStack::Iter iter;
387     iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
388     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
389         SkPath entryPath;
390         if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
391             outClipPath->reset();
392             outClipPath->setFillType(SkPath::kInverseWinding_FillType);
393             continue;
394         } else {
395             clipEntry->asPath(&entryPath);
396         }
397         entryPath.transform(transform);
398 
399         if (SkRegion::kReplace_Op == clipEntry->getOp()) {
400             *outClipPath = entryPath;
401         } else {
402             SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
403             if (!Op(*outClipPath, entryPath, op, outClipPath)) {
404                 return false;
405             }
406         }
407     }
408 
409     if (outClipPath->isInverseFillType()) {
410         // The bounds are slightly outset to ensure this is correct in the
411         // face of floating-point accuracy and possible SkRegion bitmap
412         // approximations.
413         SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
414         clipBounds.outset(SK_Scalar1, SK_Scalar1);
415         if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
416             return false;
417         }
418     }
419     return true;
420 }
421 #endif
422 
423 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
424 // graphic state stack, and the fact that we can know all the clips used
425 // on the page to optimize this.
updateClip(const SkClipStack & clipStack,const SkRegion & clipRegion,const SkPoint & translation)426 void GraphicStackState::updateClip(const SkClipStack& clipStack,
427                                    const SkRegion& clipRegion,
428                                    const SkPoint& translation) {
429     if (clipStack == currentEntry()->fClipStack) {
430         return;
431     }
432 
433     while (fStackDepth > 0) {
434         pop();
435         if (clipStack == currentEntry()->fClipStack) {
436             return;
437         }
438     }
439     push();
440 
441     currentEntry()->fClipStack = clipStack;
442     currentEntry()->fClipRegion = clipRegion;
443 
444     SkMatrix transform;
445     transform.setTranslate(translation.fX, translation.fY);
446 
447 #ifdef SK_PDF_USE_PATHOPS_CLIPPING
448     SkPath clipPath;
449     if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) {
450         emit_clip(&clipPath, nullptr, fContentStream);
451         return;
452     }
453 #endif
454     // gsState->initialEntry()->fClipStack/Region specifies the clip that has
455     // already been applied.  (If this is a top level device, then it specifies
456     // a clip to the content area.  If this is a layer, then it specifies
457     // the clip in effect when the layer was created.)  There's no need to
458     // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
459     // initial clip on the parent layer.  (This means there's a bug if the user
460     // expands the clip and then uses any xfer mode that uses dst:
461     // http://code.google.com/p/skia/issues/detail?id=228 )
462     SkClipStack::Iter iter;
463     skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
464 
465     // If the clip stack does anything other than intersect or if it uses
466     // an inverse fill type, we have to fall back to the clip region.
467     bool needRegion = false;
468     const SkClipStack::Element* clipEntry;
469     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
470         if (clipEntry->getOp() != SkRegion::kIntersect_Op ||
471                 clipEntry->isInverseFilled()) {
472             needRegion = true;
473             break;
474         }
475     }
476 
477     if (needRegion) {
478         SkPath clipPath;
479         SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
480         emit_clip(&clipPath, nullptr, fContentStream);
481     } else {
482         skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
483         const SkClipStack::Element* clipEntry;
484         for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
485             SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
486             switch (clipEntry->getType()) {
487                 case SkClipStack::Element::kRect_Type: {
488                     SkRect translatedClip;
489                     transform.mapRect(&translatedClip, clipEntry->getRect());
490                     emit_clip(nullptr, &translatedClip, fContentStream);
491                     break;
492                 }
493                 default: {
494                     SkPath translatedPath;
495                     clipEntry->asPath(&translatedPath);
496                     translatedPath.transform(transform, &translatedPath);
497                     emit_clip(&translatedPath, nullptr, fContentStream);
498                     break;
499                 }
500             }
501         }
502     }
503 }
504 
updateMatrix(const SkMatrix & matrix)505 void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
506     if (matrix == currentEntry()->fMatrix) {
507         return;
508     }
509 
510     if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
511         SkASSERT(fStackDepth > 0);
512         SkASSERT(fEntries[fStackDepth].fClipStack ==
513                  fEntries[fStackDepth -1].fClipStack);
514         pop();
515 
516         SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
517     }
518     if (matrix.getType() == SkMatrix::kIdentity_Mask) {
519         return;
520     }
521 
522     push();
523     SkPDFUtils::AppendTransform(matrix, fContentStream);
524     currentEntry()->fMatrix = matrix;
525 }
526 
updateDrawingState(const GraphicStateEntry & state)527 void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
528     // PDF treats a shader as a color, so we only set one or the other.
529     if (state.fShaderIndex >= 0) {
530         if (state.fShaderIndex != currentEntry()->fShaderIndex) {
531             SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream);
532             currentEntry()->fShaderIndex = state.fShaderIndex;
533         }
534     } else {
535         if (state.fColor != currentEntry()->fColor ||
536                 currentEntry()->fShaderIndex >= 0) {
537             emit_pdf_color(state.fColor, fContentStream);
538             fContentStream->writeText("RG ");
539             emit_pdf_color(state.fColor, fContentStream);
540             fContentStream->writeText("rg\n");
541             currentEntry()->fColor = state.fColor;
542             currentEntry()->fShaderIndex = -1;
543         }
544     }
545 
546     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
547         SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
548         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
549     }
550 
551     if (state.fTextScaleX) {
552         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
553             SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
554                                             SkIntToScalar(100));
555             SkPDFUtils::AppendScalar(pdfScale, fContentStream);
556             fContentStream->writeText(" Tz\n");
557             currentEntry()->fTextScaleX = state.fTextScaleX;
558         }
559         if (state.fTextFill != currentEntry()->fTextFill) {
560             static_assert(SkPaint::kFill_Style == 0, "enum_must_match_value");
561             static_assert(SkPaint::kStroke_Style == 1, "enum_must_match_value");
562             static_assert(SkPaint::kStrokeAndFill_Style == 2, "enum_must_match_value");
563             fContentStream->writeDecAsText(state.fTextFill);
564             fContentStream->writeText(" Tr\n");
565             currentEntry()->fTextFill = state.fTextFill;
566         }
567     }
568 }
569 
not_supported_for_layers(const SkPaint & layerPaint)570 static bool not_supported_for_layers(const SkPaint& layerPaint) {
571     // PDF does not support image filters, so render them on CPU.
572     // Note that this rendering is done at "screen" resolution (100dpi), not
573     // printer resolution.
574     // TODO: It may be possible to express some filters natively using PDF
575     // to improve quality and file size (https://bug.skia.org/3043)
576 
577     // TODO: should we return true if there is a colorfilter?
578     return layerPaint.getImageFilter() != nullptr;
579 }
580 
onCreateDevice(const CreateInfo & cinfo,const SkPaint * layerPaint)581 SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
582     if (cinfo.fForImageFilter ||
583         (layerPaint && not_supported_for_layers(*layerPaint))) {
584         return nullptr;
585     }
586     SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height());
587     return SkPDFDevice::Create(size, fRasterDpi, fCanon);
588 }
589 
590 
591 struct ContentEntry {
592     GraphicStateEntry fState;
593     SkDynamicMemoryWStream fContent;
594     SkAutoTDelete<ContentEntry> fNext;
595 
596     // If the stack is too deep we could get Stack Overflow.
597     // So we manually destruct the object.
~ContentEntryContentEntry598     ~ContentEntry() {
599         ContentEntry* val = fNext.detach();
600         while (val != nullptr) {
601             ContentEntry* valNext = val->fNext.detach();
602             // When the destructor is called, fNext is nullptr and exits.
603             delete val;
604             val = valNext;
605         }
606     }
607 };
608 
609 // A helper class to automatically finish a ContentEntry at the end of a
610 // drawing method and maintain the state needed between set up and finish.
611 class ScopedContentEntry {
612 public:
ScopedContentEntry(SkPDFDevice * device,const SkDraw & draw,const SkPaint & paint,bool hasText=false)613     ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw,
614                        const SkPaint& paint, bool hasText = false)
615         : fDevice(device),
616           fContentEntry(nullptr),
617           fXfermode(SkXfermode::kSrcOver_Mode),
618           fDstFormXObject(nullptr) {
619         init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
620     }
ScopedContentEntry(SkPDFDevice * device,const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText=false)621     ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
622                        const SkRegion& clipRegion, const SkMatrix& matrix,
623                        const SkPaint& paint, bool hasText = false)
624         : fDevice(device),
625           fContentEntry(nullptr),
626           fXfermode(SkXfermode::kSrcOver_Mode),
627           fDstFormXObject(nullptr) {
628         init(clipStack, clipRegion, matrix, paint, hasText);
629     }
630 
~ScopedContentEntry()631     ~ScopedContentEntry() {
632         if (fContentEntry) {
633             SkPath* shape = &fShape;
634             if (shape->isEmpty()) {
635                 shape = nullptr;
636             }
637             fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape);
638         }
639         SkSafeUnref(fDstFormXObject);
640     }
641 
entry()642     ContentEntry* entry() { return fContentEntry; }
643 
644     /* Returns true when we explicitly need the shape of the drawing. */
needShape()645     bool needShape() {
646         switch (fXfermode) {
647             case SkXfermode::kClear_Mode:
648             case SkXfermode::kSrc_Mode:
649             case SkXfermode::kSrcIn_Mode:
650             case SkXfermode::kSrcOut_Mode:
651             case SkXfermode::kDstIn_Mode:
652             case SkXfermode::kDstOut_Mode:
653             case SkXfermode::kSrcATop_Mode:
654             case SkXfermode::kDstATop_Mode:
655             case SkXfermode::kModulate_Mode:
656                 return true;
657             default:
658                 return false;
659         }
660     }
661 
662     /* Returns true unless we only need the shape of the drawing. */
needSource()663     bool needSource() {
664         if (fXfermode == SkXfermode::kClear_Mode) {
665             return false;
666         }
667         return true;
668     }
669 
670     /* If the shape is different than the alpha component of the content, then
671      * setShape should be called with the shape.  In particular, images and
672      * devices have rectangular shape.
673      */
setShape(const SkPath & shape)674     void setShape(const SkPath& shape) {
675         fShape = shape;
676     }
677 
678 private:
679     SkPDFDevice* fDevice;
680     ContentEntry* fContentEntry;
681     SkXfermode::Mode fXfermode;
682     SkPDFFormXObject* fDstFormXObject;
683     SkPath fShape;
684 
init(const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText)685     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
686               const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
687         // Shape has to be flatten before we get here.
688         if (matrix.hasPerspective()) {
689             NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
690             return;
691         }
692         if (paint.getXfermode()) {
693             paint.getXfermode()->asMode(&fXfermode);
694         }
695         fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
696                                                    matrix, paint, hasText,
697                                                    &fDstFormXObject);
698     }
699 };
700 
701 ////////////////////////////////////////////////////////////////////////////////
702 
SkPDFDevice(SkISize pageSize,SkScalar rasterDpi,SkPDFCanon * canon,bool flip)703 SkPDFDevice::SkPDFDevice(SkISize pageSize, SkScalar rasterDpi, SkPDFCanon* canon, bool flip)
704     : INHERITED(SkSurfaceProps(0, kUnknown_SkPixelGeometry))
705     , fPageSize(pageSize)
706     , fContentSize(pageSize)
707     , fExistingClipRegion(SkIRect::MakeSize(pageSize))
708     , fLastContentEntry(nullptr)
709     , fLastMarginContentEntry(nullptr)
710     , fDrawingArea(kContent_DrawingArea)
711     , fClipStack(nullptr)
712     , fFontGlyphUsage(new SkPDFGlyphSetMap)
713     , fRasterDpi(rasterDpi)
714     , fCanon(canon) {
715     SkASSERT(pageSize.width() > 0);
716     SkASSERT(pageSize.height() > 0);
717     fLegacyBitmap.setInfo(
718             SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()));
719     if (flip) {
720         // Skia generally uses the top left as the origin but PDF
721         // natively has the origin at the bottom left. This matrix
722         // corrects for that.  But that only needs to be done once, we
723         // don't do it when layering.
724         fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
725         fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
726     } else {
727         fInitialTransform.setIdentity();
728     }
729 }
730 
~SkPDFDevice()731 SkPDFDevice::~SkPDFDevice() {
732     this->cleanUp(true);
733 }
734 
init()735 void SkPDFDevice::init() {
736     fContentEntries.free();
737     fLastContentEntry = nullptr;
738     fMarginContentEntries.free();
739     fLastMarginContentEntry = nullptr;
740     fDrawingArea = kContent_DrawingArea;
741     if (fFontGlyphUsage.get() == nullptr) {
742         fFontGlyphUsage.reset(new SkPDFGlyphSetMap);
743     }
744 }
745 
cleanUp(bool clearFontUsage)746 void SkPDFDevice::cleanUp(bool clearFontUsage) {
747     fGraphicStateResources.unrefAll();
748     fXObjectResources.unrefAll();
749     fFontResources.unrefAll();
750     fShaderResources.unrefAll();
751     fLinkToURLs.deleteAll();
752     fLinkToDestinations.deleteAll();
753     fNamedDestinations.deleteAll();
754 
755     if (clearFontUsage) {
756         fFontGlyphUsage->reset();
757     }
758 }
759 
drawPaint(const SkDraw & d,const SkPaint & paint)760 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
761     SkPaint newPaint = paint;
762     replace_srcmode_on_opaque_paint(&newPaint);
763 
764     newPaint.setStyle(SkPaint::kFill_Style);
765     ScopedContentEntry content(this, d, newPaint);
766     internalDrawPaint(newPaint, content.entry());
767 }
768 
internalDrawPaint(const SkPaint & paint,ContentEntry * contentEntry)769 void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
770                                     ContentEntry* contentEntry) {
771     if (!contentEntry) {
772         return;
773     }
774     SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
775                                  SkIntToScalar(this->height()));
776     SkMatrix inverse;
777     if (!contentEntry->fState.fMatrix.invert(&inverse)) {
778         return;
779     }
780     inverse.mapRect(&bbox);
781 
782     SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
783     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
784                           &contentEntry->fContent);
785 }
786 
drawPoints(const SkDraw & d,SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & srcPaint)787 void SkPDFDevice::drawPoints(const SkDraw& d,
788                              SkCanvas::PointMode mode,
789                              size_t count,
790                              const SkPoint* points,
791                              const SkPaint& srcPaint) {
792     SkPaint passedPaint = srcPaint;
793     replace_srcmode_on_opaque_paint(&passedPaint);
794 
795     if (count == 0) {
796         return;
797     }
798 
799     if (SkAnnotation* annotation = passedPaint.getAnnotation()) {
800         if (handlePointAnnotation(points, count, *d.fMatrix, annotation)) {
801             return;
802         }
803     }
804 
805     // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
806     // We only use this when there's a path effect because of the overhead
807     // of multiple calls to setUpContentEntry it causes.
808     if (passedPaint.getPathEffect()) {
809         if (d.fClip->isEmpty()) {
810             return;
811         }
812         SkDraw pointDraw(d);
813         pointDraw.fDevice = this;
814         pointDraw.drawPoints(mode, count, points, passedPaint, true);
815         return;
816     }
817 
818     const SkPaint* paint = &passedPaint;
819     SkPaint modifiedPaint;
820 
821     if (mode == SkCanvas::kPoints_PointMode &&
822             paint->getStrokeCap() != SkPaint::kRound_Cap) {
823         modifiedPaint = *paint;
824         paint = &modifiedPaint;
825         if (paint->getStrokeWidth()) {
826             // PDF won't draw a single point with square/butt caps because the
827             // orientation is ambiguous.  Draw a rectangle instead.
828             modifiedPaint.setStyle(SkPaint::kFill_Style);
829             SkScalar strokeWidth = paint->getStrokeWidth();
830             SkScalar halfStroke = SkScalarHalf(strokeWidth);
831             for (size_t i = 0; i < count; i++) {
832                 SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
833                 r.inset(-halfStroke, -halfStroke);
834                 drawRect(d, r, modifiedPaint);
835             }
836             return;
837         } else {
838             modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
839         }
840     }
841 
842     ScopedContentEntry content(this, d, *paint);
843     if (!content.entry()) {
844         return;
845     }
846 
847     switch (mode) {
848         case SkCanvas::kPolygon_PointMode:
849             SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
850                                &content.entry()->fContent);
851             for (size_t i = 1; i < count; i++) {
852                 SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
853                                        &content.entry()->fContent);
854             }
855             SkPDFUtils::StrokePath(&content.entry()->fContent);
856             break;
857         case SkCanvas::kLines_PointMode:
858             for (size_t i = 0; i < count/2; i++) {
859                 SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
860                                    &content.entry()->fContent);
861                 SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
862                                        points[i * 2 + 1].fY,
863                                        &content.entry()->fContent);
864                 SkPDFUtils::StrokePath(&content.entry()->fContent);
865             }
866             break;
867         case SkCanvas::kPoints_PointMode:
868             SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
869             for (size_t i = 0; i < count; i++) {
870                 SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
871                                    &content.entry()->fContent);
872                 SkPDFUtils::ClosePath(&content.entry()->fContent);
873                 SkPDFUtils::StrokePath(&content.entry()->fContent);
874             }
875             break;
876         default:
877             SkASSERT(false);
878     }
879 }
880 
create_link_annotation(const SkRect & translatedRect)881 static SkPDFDict* create_link_annotation(const SkRect& translatedRect) {
882     SkAutoTUnref<SkPDFDict> annotation(new SkPDFDict("Annot"));
883     annotation->insertName("Subtype", "Link");
884 
885     SkAutoTUnref<SkPDFArray> border(new SkPDFArray);
886     border->reserve(3);
887     border->appendInt(0);  // Horizontal corner radius.
888     border->appendInt(0);  // Vertical corner radius.
889     border->appendInt(0);  // Width, 0 = no border.
890     annotation->insertObject("Border", border.detach());
891 
892     SkAutoTUnref<SkPDFArray> rect(new SkPDFArray);
893     rect->reserve(4);
894     rect->appendScalar(translatedRect.fLeft);
895     rect->appendScalar(translatedRect.fTop);
896     rect->appendScalar(translatedRect.fRight);
897     rect->appendScalar(translatedRect.fBottom);
898     annotation->insertObject("Rect", rect.detach());
899 
900     return annotation.detach();
901 }
902 
create_link_to_url(const SkData * urlData,const SkRect & r)903 static SkPDFDict* create_link_to_url(const SkData* urlData, const SkRect& r) {
904     SkAutoTUnref<SkPDFDict> annotation(create_link_annotation(r));
905 
906     SkString url(static_cast<const char *>(urlData->data()),
907                  urlData->size() - 1);
908     SkAutoTUnref<SkPDFDict> action(new SkPDFDict("Action"));
909     action->insertName("S", "URI");
910     action->insertString("URI", url);
911     annotation->insertObject("A", action.detach());
912     return annotation.detach();
913 }
914 
create_link_named_dest(const SkData * nameData,const SkRect & r)915 static SkPDFDict* create_link_named_dest(const SkData* nameData,
916                                          const SkRect& r) {
917     SkAutoTUnref<SkPDFDict> annotation(create_link_annotation(r));
918     SkString name(static_cast<const char *>(nameData->data()),
919                   nameData->size() - 1);
920     annotation->insertName("Dest", name);
921     return annotation.detach();
922 }
923 
drawRect(const SkDraw & d,const SkRect & rect,const SkPaint & srcPaint)924 void SkPDFDevice::drawRect(const SkDraw& d,
925                            const SkRect& rect,
926                            const SkPaint& srcPaint) {
927     SkPaint paint = srcPaint;
928     replace_srcmode_on_opaque_paint(&paint);
929     SkRect r = rect;
930     r.sort();
931 
932     if (paint.getPathEffect()) {
933         if (d.fClip->isEmpty()) {
934             return;
935         }
936         SkPath path;
937         path.addRect(r);
938         drawPath(d, path, paint, nullptr, true);
939         return;
940     }
941 
942     if (SkAnnotation* annotation = paint.getAnnotation()) {
943         SkPath path;
944         path.addRect(rect);
945         if (handlePathAnnotation(path, d, annotation)) {
946             return;
947         }
948     }
949 
950     ScopedContentEntry content(this, d, paint);
951     if (!content.entry()) {
952         return;
953     }
954     SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
955     SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
956                           &content.entry()->fContent);
957 }
958 
drawRRect(const SkDraw & draw,const SkRRect & rrect,const SkPaint & srcPaint)959 void SkPDFDevice::drawRRect(const SkDraw& draw,
960                             const SkRRect& rrect,
961                             const SkPaint& srcPaint) {
962     SkPaint paint = srcPaint;
963     replace_srcmode_on_opaque_paint(&paint);
964     SkPath  path;
965     path.addRRect(rrect);
966     this->drawPath(draw, path, paint, nullptr, true);
967 }
968 
drawOval(const SkDraw & draw,const SkRect & oval,const SkPaint & srcPaint)969 void SkPDFDevice::drawOval(const SkDraw& draw,
970                            const SkRect& oval,
971                            const SkPaint& srcPaint) {
972     SkPaint paint = srcPaint;
973     replace_srcmode_on_opaque_paint(&paint);
974     SkPath  path;
975     path.addOval(oval);
976     this->drawPath(draw, path, paint, nullptr, true);
977 }
978 
drawPath(const SkDraw & d,const SkPath & origPath,const SkPaint & srcPaint,const SkMatrix * prePathMatrix,bool pathIsMutable)979 void SkPDFDevice::drawPath(const SkDraw& d,
980                            const SkPath& origPath,
981                            const SkPaint& srcPaint,
982                            const SkMatrix* prePathMatrix,
983                            bool pathIsMutable) {
984     SkPaint paint = srcPaint;
985     replace_srcmode_on_opaque_paint(&paint);
986     SkPath modifiedPath;
987     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
988 
989     SkMatrix matrix = *d.fMatrix;
990     if (prePathMatrix) {
991         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
992             if (!pathIsMutable) {
993                 pathPtr = &modifiedPath;
994                 pathIsMutable = true;
995             }
996             origPath.transform(*prePathMatrix, pathPtr);
997         } else {
998             matrix.preConcat(*prePathMatrix);
999         }
1000     }
1001 
1002     if (paint.getPathEffect()) {
1003         if (d.fClip->isEmpty()) {
1004             return;
1005         }
1006         if (!pathIsMutable) {
1007             pathPtr = &modifiedPath;
1008             pathIsMutable = true;
1009         }
1010         bool fill = paint.getFillPath(origPath, pathPtr);
1011 
1012         SkPaint noEffectPaint(paint);
1013         noEffectPaint.setPathEffect(nullptr);
1014         if (fill) {
1015             noEffectPaint.setStyle(SkPaint::kFill_Style);
1016         } else {
1017             noEffectPaint.setStyle(SkPaint::kStroke_Style);
1018             noEffectPaint.setStrokeWidth(0);
1019         }
1020         drawPath(d, *pathPtr, noEffectPaint, nullptr, true);
1021         return;
1022     }
1023 
1024     if (handleInversePath(d, origPath, paint, pathIsMutable, prePathMatrix)) {
1025         return;
1026     }
1027 
1028     if (SkAnnotation* annotation = paint.getAnnotation()) {
1029         if (handlePathAnnotation(*pathPtr, d, annotation)) {
1030             return;
1031         }
1032     }
1033 
1034     ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
1035     if (!content.entry()) {
1036         return;
1037     }
1038     bool consumeDegeratePathSegments =
1039            paint.getStyle() == SkPaint::kFill_Style ||
1040            (paint.getStrokeCap() != SkPaint::kRound_Cap &&
1041             paint.getStrokeCap() != SkPaint::kSquare_Cap);
1042     SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
1043                          consumeDegeratePathSegments,
1044                          &content.entry()->fContent);
1045     SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
1046                           &content.entry()->fContent);
1047 }
1048 
drawBitmapRect(const SkDraw & draw,const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint & srcPaint,SkCanvas::SrcRectConstraint constraint)1049 void SkPDFDevice::drawBitmapRect(const SkDraw& draw,
1050                                  const SkBitmap& bitmap,
1051                                  const SkRect* src,
1052                                  const SkRect& dst,
1053                                  const SkPaint& srcPaint,
1054                                  SkCanvas::SrcRectConstraint constraint) {
1055     const SkImage* image = fCanon->bitmapToImage(bitmap);
1056     if (!image) {
1057         return;
1058     }
1059     // ownership of this image is retained by the canon.
1060     this->drawImageRect(draw, image, src, dst, srcPaint, constraint);
1061 }
1062 
drawBitmap(const SkDraw & d,const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint & srcPaint)1063 void SkPDFDevice::drawBitmap(const SkDraw& d,
1064                              const SkBitmap& bitmap,
1065                              const SkMatrix& matrix,
1066                              const SkPaint& srcPaint) {
1067     SkPaint paint = srcPaint;
1068     if (bitmap.isOpaque()) {
1069         replace_srcmode_on_opaque_paint(&paint);
1070     }
1071 
1072     if (d.fClip->isEmpty()) {
1073         return;
1074     }
1075 
1076     SkMatrix transform = matrix;
1077     transform.postConcat(*d.fMatrix);
1078     const SkImage* image = fCanon->bitmapToImage(bitmap);
1079     if (!image) {
1080         return;
1081     }
1082     this->internalDrawImage(transform, d.fClipStack, *d.fClip, image, nullptr,
1083                             paint);
1084 }
1085 
drawSprite(const SkDraw & d,const SkBitmap & bitmap,int x,int y,const SkPaint & srcPaint)1086 void SkPDFDevice::drawSprite(const SkDraw& d,
1087                              const SkBitmap& bitmap,
1088                              int x,
1089                              int y,
1090                              const SkPaint& srcPaint) {
1091     SkPaint paint = srcPaint;
1092     if (bitmap.isOpaque()) {
1093         replace_srcmode_on_opaque_paint(&paint);
1094     }
1095 
1096     if (d.fClip->isEmpty()) {
1097         return;
1098     }
1099 
1100     SkMatrix matrix;
1101     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
1102     const SkImage* image = fCanon->bitmapToImage(bitmap);
1103     if (!image) {
1104         return;
1105     }
1106     this->internalDrawImage(matrix, d.fClipStack, *d.fClip, image, nullptr,
1107                             paint);
1108 }
1109 
drawImage(const SkDraw & draw,const SkImage * image,SkScalar x,SkScalar y,const SkPaint & srcPaint)1110 void SkPDFDevice::drawImage(const SkDraw& draw,
1111                             const SkImage* image,
1112                             SkScalar x,
1113                             SkScalar y,
1114                             const SkPaint& srcPaint) {
1115     SkPaint paint = srcPaint;
1116     if (!image) {
1117         return;
1118     }
1119     if (image->isOpaque()) {
1120         replace_srcmode_on_opaque_paint(&paint);
1121     }
1122     if (draw.fClip->isEmpty()) {
1123         return;
1124     }
1125     SkMatrix transform = SkMatrix::MakeTrans(x, y);
1126     transform.postConcat(*draw.fMatrix);
1127     this->internalDrawImage(transform, draw.fClipStack, *draw.fClip, image,
1128                             nullptr, paint);
1129 }
1130 
drawImageRect(const SkDraw & draw,const SkImage * image,const SkRect * src,const SkRect & dst,const SkPaint & srcPaint,SkCanvas::SrcRectConstraint constraint)1131 void SkPDFDevice::drawImageRect(const SkDraw& draw,
1132                                 const SkImage* image,
1133                                 const SkRect* src,
1134                                 const SkRect& dst,
1135                                 const SkPaint& srcPaint,
1136                                 SkCanvas::SrcRectConstraint constraint) {
1137     if (!image) {
1138         return;
1139     }
1140     if (draw.fClip->isEmpty()) {
1141         return;
1142     }
1143     SkPaint paint = srcPaint;
1144     if (image->isOpaque()) {
1145         replace_srcmode_on_opaque_paint(&paint);
1146     }
1147     // TODO: this code path must be updated to respect the flags parameter
1148     SkMatrix matrix;
1149     SkRect tmpSrc, tmpDst;
1150     SkRect imageBounds = SkRect::Make(image->bounds());
1151 
1152     // Compute matrix from the two rectangles
1153     if (src) {
1154         tmpSrc = *src;
1155     } else {
1156         tmpSrc = imageBounds;
1157     }
1158     matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1159 
1160     // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
1161     // needed (if the src was clipped). No check needed if src==null.
1162     SkAutoTUnref<const SkImage> autoImageUnref;
1163     if (src) {
1164         if (!imageBounds.contains(*src)) {
1165             if (!tmpSrc.intersect(imageBounds)) {
1166                 return; // nothing to draw
1167             }
1168             // recompute dst, based on the smaller tmpSrc
1169             matrix.mapRect(&tmpDst, tmpSrc);
1170         }
1171 
1172         // since we may need to clamp to the borders of the src rect within
1173         // the bitmap, we extract a subset.
1174         SkIRect srcIR;
1175         tmpSrc.roundOut(&srcIR);
1176 
1177         autoImageUnref.reset(image->newSubset(srcIR));
1178         if (!autoImageUnref) {
1179             return;
1180         }
1181         image = autoImageUnref;
1182         // Since we did an extract, we need to adjust the matrix accordingly
1183         SkScalar dx = 0, dy = 0;
1184         if (srcIR.fLeft > 0) {
1185             dx = SkIntToScalar(srcIR.fLeft);
1186         }
1187         if (srcIR.fTop > 0) {
1188             dy = SkIntToScalar(srcIR.fTop);
1189         }
1190         if (dx || dy) {
1191             matrix.preTranslate(dx, dy);
1192         }
1193     }
1194     matrix.postConcat(*draw.fMatrix);
1195     this->internalDrawImage(matrix, draw.fClipStack, *draw.fClip, image,
1196                             nullptr, paint);
1197 }
1198 
1199 //  Create a PDF string. Maximum length (in bytes) is 65,535.
1200 //  @param input     A string value.
1201 //  @param len       The length of the input array.
1202 //  @param wideChars True iff the upper byte in each uint16_t is
1203 //                   significant and should be encoded and not
1204 //                   discarded.  If true, the upper byte is encoded
1205 //                   first.  Otherwise, we assert the upper byte is
1206 //                   zero.
format_wide_string(const uint16_t * input,size_t len,bool wideChars)1207 static SkString format_wide_string(const uint16_t* input,
1208                                    size_t len,
1209                                    bool wideChars) {
1210     if (wideChars) {
1211         SkASSERT(2 * len < 65535);
1212         static const char gHex[] = "0123456789ABCDEF";
1213         SkString result(4 * len + 2);
1214         result[0] = '<';
1215         for (size_t i = 0; i < len; i++) {
1216             result[4 * i + 1] = gHex[(input[i] >> 12) & 0xF];
1217             result[4 * i + 2] = gHex[(input[i] >>  8) & 0xF];
1218             result[4 * i + 3] = gHex[(input[i] >>  4) & 0xF];
1219             result[4 * i + 4] = gHex[(input[i]      ) & 0xF];
1220         }
1221         result[4 * len + 1] = '>';
1222         return result;
1223     } else {
1224         SkASSERT(len <= 65535);
1225         SkString tmp(len);
1226         for (size_t i = 0; i < len; i++) {
1227             SkASSERT(0 == input[i] >> 8);
1228             tmp[i] = static_cast<uint8_t>(input[i]);
1229         }
1230         return SkPDFUtils::FormatString(tmp.c_str(), tmp.size());
1231     }
1232 }
1233 
draw_transparent_text(SkPDFDevice * device,const SkDraw & d,const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & srcPaint)1234 static void draw_transparent_text(SkPDFDevice* device,
1235                                   const SkDraw& d,
1236                                   const void* text, size_t len,
1237                                   SkScalar x, SkScalar y,
1238                                   const SkPaint& srcPaint) {
1239 
1240     SkPaint transparent;
1241     if (!SkPDFFont::CanEmbedTypeface(transparent.getTypeface(),
1242                                      device->getCanon())) {
1243         SkDEBUGFAIL("default typeface should be embeddable");
1244         return;  // Avoid infinite loop in release.
1245     }
1246     transparent.setTextSize(srcPaint.getTextSize());
1247     transparent.setColor(SK_ColorTRANSPARENT);
1248     switch (srcPaint.getTextEncoding()) {
1249         case SkPaint::kGlyphID_TextEncoding: {
1250             // Since a glyphId<->Unicode mapping is typeface-specific,
1251             // map back to Unicode first.
1252             size_t glyphCount = len / 2;
1253             SkAutoTMalloc<SkUnichar> unichars(glyphCount);
1254             srcPaint.glyphsToUnichars(
1255                     (const uint16_t*)text, SkToInt(glyphCount), &unichars[0]);
1256             transparent.setTextEncoding(SkPaint::kUTF32_TextEncoding);
1257             device->drawText(d, &unichars[0],
1258                              glyphCount * sizeof(SkUnichar),
1259                              x, y, transparent);
1260             break;
1261         }
1262         case SkPaint::kUTF8_TextEncoding:
1263         case SkPaint::kUTF16_TextEncoding:
1264         case SkPaint::kUTF32_TextEncoding:
1265             transparent.setTextEncoding(srcPaint.getTextEncoding());
1266             device->drawText(d, text, len, x, y, transparent);
1267             break;
1268         default:
1269             SkFAIL("unknown text encoding");
1270     }
1271 }
1272 
1273 
drawText(const SkDraw & d,const void * text,size_t len,SkScalar x,SkScalar y,const SkPaint & srcPaint)1274 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
1275                            SkScalar x, SkScalar y, const SkPaint& srcPaint) {
1276     if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) {
1277         // https://bug.skia.org/3866
1278         SkPath path;
1279         srcPaint.getTextPath(text, len, x, y, &path);
1280         this->drawPath(d, path, srcPaint, &SkMatrix::I(), true);
1281         // Draw text transparently to make it copyable/searchable/accessable.
1282         draw_transparent_text(this, d, text, len, x, y, srcPaint);
1283         return;
1284     }
1285     SkPaint paint = srcPaint;
1286     replace_srcmode_on_opaque_paint(&paint);
1287 
1288     NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
1289     if (paint.getMaskFilter() != nullptr) {
1290         // Don't pretend we support drawing MaskFilters, it makes for artifacts
1291         // making text unreadable (e.g. same text twice when using CSS shadows).
1292         return;
1293     }
1294     SkPaint textPaint = calculate_text_paint(paint);
1295     ScopedContentEntry content(this, d, textPaint, true);
1296     if (!content.entry()) {
1297         return;
1298     }
1299 
1300     SkGlyphStorage storage(0);
1301     const uint16_t* glyphIDs = nullptr;
1302     int numGlyphs = force_glyph_encoding(paint, text, len, &storage, &glyphIDs);
1303     textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
1304 
1305     SkPaint::GlyphCacheProc glyphCacheProc = textPaint.getGlyphCacheProc(true);
1306     align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
1307     content.entry()->fContent.writeText("BT\n");
1308     set_text_transform(x, y, textPaint.getTextSkewX(),
1309                        &content.entry()->fContent);
1310     int consumedGlyphCount = 0;
1311 
1312     SkTDArray<uint16_t> glyphIDsCopy(glyphIDs, numGlyphs);
1313 
1314     while (numGlyphs > consumedGlyphCount) {
1315         this->updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry());
1316         SkPDFFont* font = content.entry()->fState.fFont;
1317 
1318         int availableGlyphs = font->glyphsToPDFFontEncoding(
1319                 glyphIDsCopy.begin() + consumedGlyphCount,
1320                 numGlyphs - consumedGlyphCount);
1321         fFontGlyphUsage->noteGlyphUsage(
1322                 font,  glyphIDsCopy.begin() + consumedGlyphCount,
1323                 availableGlyphs);
1324         SkString encodedString =
1325                 format_wide_string(glyphIDsCopy.begin() + consumedGlyphCount,
1326                                    availableGlyphs, font->multiByteGlyphs());
1327         content.entry()->fContent.writeText(encodedString.c_str());
1328         consumedGlyphCount += availableGlyphs;
1329         content.entry()->fContent.writeText(" Tj\n");
1330     }
1331     content.entry()->fContent.writeText("ET\n");
1332 }
1333 
drawPosText(const SkDraw & d,const void * text,size_t len,const SkScalar pos[],int scalarsPerPos,const SkPoint & offset,const SkPaint & srcPaint)1334 void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
1335                               const SkScalar pos[], int scalarsPerPos,
1336                               const SkPoint& offset, const SkPaint& srcPaint) {
1337     if (!SkPDFFont::CanEmbedTypeface(srcPaint.getTypeface(), fCanon)) {
1338         const SkPoint* positions = reinterpret_cast<const SkPoint*>(pos);
1339         SkAutoTMalloc<SkPoint> positionsBuffer;
1340         if (2 != scalarsPerPos) {
1341             int glyphCount = srcPaint.textToGlyphs(text, len, NULL);
1342             positionsBuffer.reset(glyphCount);
1343             for (int  i = 0; i < glyphCount; ++i) {
1344                 positionsBuffer[i].set(pos[i], 0.0f);
1345             }
1346             positions = &positionsBuffer[0];
1347         }
1348         SkPath path;
1349         srcPaint.getPosTextPath(text, len, positions, &path);
1350         SkMatrix matrix;
1351         matrix.setTranslate(offset);
1352         this->drawPath(d, path, srcPaint, &matrix, true);
1353         // Draw text transparently to make it copyable/searchable/accessable.
1354         draw_transparent_text(
1355                 this, d, text, len, offset.x() + positions[0].x(),
1356                 offset.y() + positions[0].y(), srcPaint);
1357         return;
1358     }
1359 
1360     SkPaint paint = srcPaint;
1361     replace_srcmode_on_opaque_paint(&paint);
1362 
1363     NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
1364     if (paint.getMaskFilter() != nullptr) {
1365         // Don't pretend we support drawing MaskFilters, it makes for artifacts
1366         // making text unreadable (e.g. same text twice when using CSS shadows).
1367         return;
1368     }
1369     SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
1370     SkPaint textPaint = calculate_text_paint(paint);
1371     ScopedContentEntry content(this, d, textPaint, true);
1372     if (!content.entry()) {
1373         return;
1374     }
1375 
1376     SkGlyphStorage storage(0);
1377     const uint16_t* glyphIDs = nullptr;
1378     size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage, &glyphIDs);
1379     textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
1380 
1381     SkPaint::GlyphCacheProc glyphCacheProc = textPaint.getGlyphCacheProc(true);
1382     content.entry()->fContent.writeText("BT\n");
1383     this->updateFont(textPaint, glyphIDs[0], content.entry());
1384     for (size_t i = 0; i < numGlyphs; i++) {
1385         SkPDFFont* font = content.entry()->fState.fFont;
1386         uint16_t encodedValue = glyphIDs[i];
1387         if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
1388             // The current pdf font cannot encode the current glyph.
1389             // Try to get a pdf font which can encode the current glyph.
1390             this->updateFont(textPaint, glyphIDs[i], content.entry());
1391             font = content.entry()->fState.fFont;
1392             if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
1393                 SkDEBUGFAIL("PDF could not encode glyph.");
1394                 continue;
1395             }
1396         }
1397 
1398         fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1);
1399         SkScalar x = offset.x() + pos[i * scalarsPerPos];
1400         SkScalar y = offset.y() + (2 == scalarsPerPos ? pos[i * scalarsPerPos + 1] : 0);
1401 
1402         align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y);
1403         set_text_transform(x, y, textPaint.getTextSkewX(), &content.entry()->fContent);
1404         SkString encodedString =
1405                 format_wide_string(&encodedValue, 1, font->multiByteGlyphs());
1406         content.entry()->fContent.writeText(encodedString.c_str());
1407         content.entry()->fContent.writeText(" Tj\n");
1408     }
1409     content.entry()->fContent.writeText("ET\n");
1410 }
1411 
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)1412 void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
1413                                int vertexCount, const SkPoint verts[],
1414                                const SkPoint texs[], const SkColor colors[],
1415                                SkXfermode* xmode, const uint16_t indices[],
1416                                int indexCount, const SkPaint& paint) {
1417     if (d.fClip->isEmpty()) {
1418         return;
1419     }
1420     // TODO: implement drawVertices
1421 }
1422 
1423 struct RectWithData {
1424     SkRect rect;
1425     SkAutoTUnref<const SkData> data;
1426 
RectWithDataRectWithData1427     RectWithData(const SkRect& rect, const SkData* data)
1428         : rect(rect), data(SkRef(data)) {}
1429 };
1430 
1431 struct NamedDestination {
1432     SkAutoTUnref<const SkData> nameData;
1433     SkPoint point;
1434 
NamedDestinationNamedDestination1435     NamedDestination(const SkData* nameData, const SkPoint& point)
1436         : nameData(SkRef(nameData)), point(point) {}
1437 };
1438 
drawDevice(const SkDraw & d,SkBaseDevice * device,int x,int y,const SkPaint & paint)1439 void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device,
1440                              int x, int y, const SkPaint& paint) {
1441     // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses.
1442     SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
1443 
1444     SkScalar scalarX = SkIntToScalar(x);
1445     SkScalar scalarY = SkIntToScalar(y);
1446     for (RectWithData* link : pdfDevice->fLinkToURLs) {
1447         fLinkToURLs.push(new RectWithData(
1448                 link->rect.makeOffset(scalarX, scalarY), link->data));
1449     }
1450     for (RectWithData* link : pdfDevice->fLinkToDestinations) {
1451         fLinkToDestinations.push(new RectWithData(
1452                 link->rect.makeOffset(scalarX, scalarY), link->data));
1453     }
1454     for (NamedDestination* d : pdfDevice->fNamedDestinations) {
1455         fNamedDestinations.push(new NamedDestination(
1456                 d->nameData, d->point + SkPoint::Make(scalarX, scalarY)));
1457     }
1458 
1459     if (pdfDevice->isContentEmpty()) {
1460         return;
1461     }
1462 
1463     SkMatrix matrix;
1464     matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
1465     ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
1466     if (!content.entry()) {
1467         return;
1468     }
1469     if (content.needShape()) {
1470         SkPath shape;
1471         shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
1472                                        SkIntToScalar(device->width()),
1473                                        SkIntToScalar(device->height())));
1474         content.setShape(shape);
1475     }
1476     if (!content.needSource()) {
1477         return;
1478     }
1479 
1480     SkAutoTUnref<SkPDFFormXObject> xObject(new SkPDFFormXObject(pdfDevice));
1481     SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
1482                                 &content.entry()->fContent);
1483 
1484     // Merge glyph sets from the drawn device.
1485     fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage());
1486 }
1487 
imageInfo() const1488 SkImageInfo SkPDFDevice::imageInfo() const {
1489     return fLegacyBitmap.info();
1490 }
1491 
onAttachToCanvas(SkCanvas * canvas)1492 void SkPDFDevice::onAttachToCanvas(SkCanvas* canvas) {
1493     INHERITED::onAttachToCanvas(canvas);
1494 
1495     // Canvas promises that this ptr is valid until onDetachFromCanvas is called
1496     fClipStack = canvas->getClipStack();
1497 }
1498 
onDetachFromCanvas()1499 void SkPDFDevice::onDetachFromCanvas() {
1500     INHERITED::onDetachFromCanvas();
1501 
1502     fClipStack = nullptr;
1503 }
1504 
newSurface(const SkImageInfo & info,const SkSurfaceProps & props)1505 SkSurface* SkPDFDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1506     return SkSurface::NewRaster(info, &props);
1507 }
1508 
getLastContentEntry()1509 ContentEntry* SkPDFDevice::getLastContentEntry() {
1510     if (fDrawingArea == kContent_DrawingArea) {
1511         return fLastContentEntry;
1512     } else {
1513         return fLastMarginContentEntry;
1514     }
1515 }
1516 
getContentEntries()1517 SkAutoTDelete<ContentEntry>* SkPDFDevice::getContentEntries() {
1518     if (fDrawingArea == kContent_DrawingArea) {
1519         return &fContentEntries;
1520     } else {
1521         return &fMarginContentEntries;
1522     }
1523 }
1524 
setLastContentEntry(ContentEntry * contentEntry)1525 void SkPDFDevice::setLastContentEntry(ContentEntry* contentEntry) {
1526     if (fDrawingArea == kContent_DrawingArea) {
1527         fLastContentEntry = contentEntry;
1528     } else {
1529         fLastMarginContentEntry = contentEntry;
1530     }
1531 }
1532 
setDrawingArea(DrawingArea drawingArea)1533 void SkPDFDevice::setDrawingArea(DrawingArea drawingArea) {
1534     // A ScopedContentEntry only exists during the course of a draw call, so
1535     // this can't be called while a ScopedContentEntry exists.
1536     fDrawingArea = drawingArea;
1537 }
1538 
createResourceDict() const1539 SkPDFDict* SkPDFDevice::createResourceDict() const {
1540     SkTDArray<SkPDFObject*> fonts;
1541     fonts.setReserve(fFontResources.count());
1542     for (SkPDFFont* font : fFontResources) {
1543         fonts.push(font);
1544     }
1545     return SkPDFResourceDict::Create(
1546             &fGraphicStateResources,
1547             &fShaderResources,
1548             &fXObjectResources,
1549             &fonts);
1550 }
1551 
getFontResources() const1552 const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
1553     return fFontResources;
1554 }
1555 
copyMediaBox() const1556 SkPDFArray* SkPDFDevice::copyMediaBox() const {
1557     // should this be a singleton?
1558 
1559     SkAutoTUnref<SkPDFArray> mediaBox(new SkPDFArray);
1560     mediaBox->reserve(4);
1561     mediaBox->appendInt(0);
1562     mediaBox->appendInt(0);
1563     mediaBox->appendInt(fPageSize.fWidth);
1564     mediaBox->appendInt(fPageSize.fHeight);
1565     return mediaBox.detach();
1566 }
1567 
content() const1568 SkStreamAsset* SkPDFDevice::content() const {
1569     SkDynamicMemoryWStream buffer;
1570     this->writeContent(&buffer);
1571     return buffer.bytesWritten() > 0
1572         ? buffer.detachAsStream()
1573         : new SkMemoryStream;
1574 }
1575 
copyContentEntriesToData(ContentEntry * entry,SkWStream * data) const1576 void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry,
1577         SkWStream* data) const {
1578     // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the
1579     // right thing to pass here.
1580     GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data);
1581     while (entry != nullptr) {
1582         SkPoint translation;
1583         translation.iset(this->getOrigin());
1584         translation.negate();
1585         gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
1586                            translation);
1587         gsState.updateMatrix(entry->fState.fMatrix);
1588         gsState.updateDrawingState(entry->fState);
1589 
1590         entry->fContent.writeToStream(data);
1591         entry = entry->fNext.get();
1592     }
1593     gsState.drainStack();
1594 }
1595 
writeContent(SkWStream * out) const1596 void SkPDFDevice::writeContent(SkWStream* out) const {
1597     if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
1598         SkPDFUtils::AppendTransform(fInitialTransform, out);
1599     }
1600 
1601     // TODO(aayushkumar): Apply clip along the margins.  Currently, webkit
1602     // colors the contentArea white before it starts drawing into it and
1603     // that currently acts as our clip.
1604     // Also, think about adding a transform here (or assume that the values
1605     // sent across account for that)
1606     SkPDFDevice::copyContentEntriesToData(fMarginContentEntries.get(), out);
1607 
1608     // If the content area is the entire page, then we don't need to clip
1609     // the content area (PDF area clips to the page size).  Otherwise,
1610     // we have to clip to the content area; we've already applied the
1611     // initial transform, so just clip to the device size.
1612     if (fPageSize != fContentSize) {
1613         SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()),
1614                                   SkIntToScalar(this->height()));
1615         emit_clip(nullptr, &r, out);
1616     }
1617 
1618     SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), out);
1619 }
1620 
1621 /* Draws an inverse filled path by using Path Ops to compute the positive
1622  * inverse using the current clip as the inverse bounds.
1623  * Return true if this was an inverse path and was properly handled,
1624  * otherwise returns false and the normal drawing routine should continue,
1625  * either as a (incorrect) fallback or because the path was not inverse
1626  * in the first place.
1627  */
handleInversePath(const SkDraw & d,const SkPath & origPath,const SkPaint & paint,bool pathIsMutable,const SkMatrix * prePathMatrix)1628 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath,
1629                                     const SkPaint& paint, bool pathIsMutable,
1630                                     const SkMatrix* prePathMatrix) {
1631     if (!origPath.isInverseFillType()) {
1632         return false;
1633     }
1634 
1635     if (d.fClip->isEmpty()) {
1636         return false;
1637     }
1638 
1639     SkPath modifiedPath;
1640     SkPath* pathPtr = const_cast<SkPath*>(&origPath);
1641     SkPaint noInversePaint(paint);
1642 
1643     // Merge stroking operations into final path.
1644     if (SkPaint::kStroke_Style == paint.getStyle() ||
1645         SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
1646         bool doFillPath = paint.getFillPath(origPath, &modifiedPath);
1647         if (doFillPath) {
1648             noInversePaint.setStyle(SkPaint::kFill_Style);
1649             noInversePaint.setStrokeWidth(0);
1650             pathPtr = &modifiedPath;
1651         } else {
1652             // To be consistent with the raster output, hairline strokes
1653             // are rendered as non-inverted.
1654             modifiedPath.toggleInverseFillType();
1655             drawPath(d, modifiedPath, paint, nullptr, true);
1656             return true;
1657         }
1658     }
1659 
1660     // Get bounds of clip in current transform space
1661     // (clip bounds are given in device space).
1662     SkRect bounds;
1663     SkMatrix transformInverse;
1664     SkMatrix totalMatrix = *d.fMatrix;
1665     if (prePathMatrix) {
1666         totalMatrix.preConcat(*prePathMatrix);
1667     }
1668     if (!totalMatrix.invert(&transformInverse)) {
1669         return false;
1670     }
1671     bounds.set(d.fClip->getBounds());
1672     transformInverse.mapRect(&bounds);
1673 
1674     // Extend the bounds by the line width (plus some padding)
1675     // so the edge doesn't cause a visible stroke.
1676     bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
1677                   paint.getStrokeWidth() + SK_Scalar1);
1678 
1679     if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
1680         return false;
1681     }
1682 
1683     drawPath(d, modifiedPath, noInversePaint, prePathMatrix, true);
1684     return true;
1685 }
1686 
handlePointAnnotation(const SkPoint * points,size_t count,const SkMatrix & matrix,SkAnnotation * annotationInfo)1687 bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
1688                                         const SkMatrix& matrix,
1689                                         SkAnnotation* annotationInfo) {
1690     SkData* nameData = annotationInfo->find(
1691             SkAnnotationKeys::Define_Named_Dest_Key());
1692     if (nameData) {
1693         for (size_t i = 0; i < count; i++) {
1694             SkPoint transformedPoint;
1695             matrix.mapXY(points[i].x(), points[i].y(), &transformedPoint);
1696             fNamedDestinations.push(new NamedDestination(nameData, transformedPoint));
1697         }
1698         return true;
1699     }
1700     return false;
1701 }
1702 
handlePathAnnotation(const SkPath & path,const SkDraw & d,SkAnnotation * annotation)1703 bool SkPDFDevice::handlePathAnnotation(const SkPath& path,
1704                                        const SkDraw& d,
1705                                        SkAnnotation* annotation) {
1706     SkASSERT(annotation);
1707 
1708     SkPath transformedPath = path;
1709     transformedPath.transform(*d.fMatrix);
1710     SkRasterClip clip = *d.fRC;
1711     clip.op(transformedPath, SkIRect::MakeWH(width(), height()), SkRegion::kIntersect_Op,
1712             false);
1713     SkRect transformedRect = SkRect::Make(clip.getBounds());
1714 
1715     SkData* urlData = annotation->find(SkAnnotationKeys::URL_Key());
1716     if (urlData) {
1717         if (!transformedRect.isEmpty()) {
1718             fLinkToURLs.push(new RectWithData(transformedRect, urlData));
1719         }
1720         return true;
1721     }
1722 
1723     SkData* linkToDestination =
1724             annotation->find(SkAnnotationKeys::Link_Named_Dest_Key());
1725     if (linkToDestination) {
1726         if (!transformedRect.isEmpty()) {
1727             fLinkToDestinations.push(new RectWithData(transformedRect, linkToDestination));
1728         }
1729         return true;
1730     }
1731 
1732     return false;
1733 }
1734 
appendAnnotations(SkPDFArray * array) const1735 void SkPDFDevice::appendAnnotations(SkPDFArray* array) const {
1736     array->reserve(fLinkToURLs.count() + fLinkToDestinations.count());
1737     for (RectWithData* rectWithURL : fLinkToURLs) {
1738         SkRect r;
1739         fInitialTransform.mapRect(&r, rectWithURL->rect);
1740         array->appendObject(create_link_to_url(rectWithURL->data, r));
1741     }
1742     for (RectWithData* linkToDestination : fLinkToDestinations) {
1743         SkRect r;
1744         fInitialTransform.mapRect(&r, linkToDestination->rect);
1745         array->appendObject(create_link_named_dest(linkToDestination->data, r));
1746     }
1747 }
1748 
appendDestinations(SkPDFDict * dict,SkPDFObject * page) const1749 void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
1750     for (NamedDestination* dest : fNamedDestinations) {
1751         SkAutoTUnref<SkPDFArray> pdfDest(new SkPDFArray);
1752         pdfDest->reserve(5);
1753         pdfDest->appendObjRef(SkRef(page));
1754         pdfDest->appendName("XYZ");
1755         SkPoint p = fInitialTransform.mapXY(dest->point.x(), dest->point.y());
1756         pdfDest->appendScalar(p.x());
1757         pdfDest->appendScalar(p.y());
1758         pdfDest->appendInt(0);  // Leave zoom unchanged
1759         SkString name(static_cast<const char*>(dest->nameData->data()));
1760         dict->insertObject(name, pdfDest.detach());
1761     }
1762 }
1763 
createFormXObjectFromDevice()1764 SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
1765     SkPDFFormXObject* xobject = new SkPDFFormXObject(this);
1766     // We always draw the form xobjects that we create back into the device, so
1767     // we simply preserve the font usage instead of pulling it out and merging
1768     // it back in later.
1769     cleanUp(false);  // Reset this device to have no content.
1770     init();
1771     return xobject;
1772 }
1773 
drawFormXObjectWithMask(int xObjectIndex,SkPDFFormXObject * mask,const SkClipStack * clipStack,const SkRegion & clipRegion,SkXfermode::Mode mode,bool invertClip)1774 void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
1775                                           SkPDFFormXObject* mask,
1776                                           const SkClipStack* clipStack,
1777                                           const SkRegion& clipRegion,
1778                                           SkXfermode::Mode mode,
1779                                           bool invertClip) {
1780     if (clipRegion.isEmpty() && !invertClip) {
1781         return;
1782     }
1783 
1784     SkAutoTUnref<SkPDFObject> sMaskGS(SkPDFGraphicState::GetSMaskGraphicState(
1785             mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
1786 
1787     SkMatrix identity;
1788     identity.reset();
1789     SkPaint paint;
1790     paint.setXfermodeMode(mode);
1791     ScopedContentEntry content(this, clipStack, clipRegion, identity, paint);
1792     if (!content.entry()) {
1793         return;
1794     }
1795     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1796                                   &content.entry()->fContent);
1797     SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
1798 
1799     sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
1800     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
1801                                   &content.entry()->fContent);
1802 }
1803 
setUpContentEntry(const SkClipStack * clipStack,const SkRegion & clipRegion,const SkMatrix & matrix,const SkPaint & paint,bool hasText,SkPDFFormXObject ** dst)1804 ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
1805                                              const SkRegion& clipRegion,
1806                                              const SkMatrix& matrix,
1807                                              const SkPaint& paint,
1808                                              bool hasText,
1809                                              SkPDFFormXObject** dst) {
1810     *dst = nullptr;
1811     if (clipRegion.isEmpty()) {
1812         return nullptr;
1813     }
1814 
1815     // The clip stack can come from an SkDraw where it is technically optional.
1816     SkClipStack synthesizedClipStack;
1817     if (clipStack == nullptr) {
1818         if (clipRegion == fExistingClipRegion) {
1819             clipStack = &fExistingClipStack;
1820         } else {
1821             // GraphicStackState::updateClip expects the clip stack to have
1822             // fExistingClip as a prefix, so start there, then set the clip
1823             // to the passed region.
1824             synthesizedClipStack = fExistingClipStack;
1825             SkPath clipPath;
1826             clipRegion.getBoundaryPath(&clipPath);
1827             synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op,
1828                                              false);
1829             clipStack = &synthesizedClipStack;
1830         }
1831     }
1832 
1833     SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
1834     if (paint.getXfermode()) {
1835         paint.getXfermode()->asMode(&xfermode);
1836     }
1837 
1838     // For the following modes, we want to handle source and destination
1839     // separately, so make an object of what's already there.
1840     if (xfermode == SkXfermode::kClear_Mode       ||
1841             xfermode == SkXfermode::kSrc_Mode     ||
1842             xfermode == SkXfermode::kSrcIn_Mode   ||
1843             xfermode == SkXfermode::kDstIn_Mode   ||
1844             xfermode == SkXfermode::kSrcOut_Mode  ||
1845             xfermode == SkXfermode::kDstOut_Mode  ||
1846             xfermode == SkXfermode::kSrcATop_Mode ||
1847             xfermode == SkXfermode::kDstATop_Mode ||
1848             xfermode == SkXfermode::kModulate_Mode) {
1849         if (!isContentEmpty()) {
1850             *dst = createFormXObjectFromDevice();
1851             SkASSERT(isContentEmpty());
1852         } else if (xfermode != SkXfermode::kSrc_Mode &&
1853                    xfermode != SkXfermode::kSrcOut_Mode) {
1854             // Except for Src and SrcOut, if there isn't anything already there,
1855             // then we're done.
1856             return nullptr;
1857         }
1858     }
1859     // TODO(vandebo): Figure out how/if we can handle the following modes:
1860     // Xor, Plus.
1861 
1862     // Dst xfer mode doesn't draw source at all.
1863     if (xfermode == SkXfermode::kDst_Mode) {
1864         return nullptr;
1865     }
1866 
1867     ContentEntry* entry;
1868     SkAutoTDelete<ContentEntry> newEntry;
1869 
1870     ContentEntry* lastContentEntry = getLastContentEntry();
1871     if (lastContentEntry && lastContentEntry->fContent.getOffset() == 0) {
1872         entry = lastContentEntry;
1873     } else {
1874         newEntry.reset(new ContentEntry);
1875         entry = newEntry.get();
1876     }
1877 
1878     populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
1879                                        hasText, &entry->fState);
1880     if (lastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
1881             entry->fState.compareInitialState(lastContentEntry->fState)) {
1882         return lastContentEntry;
1883     }
1884 
1885     SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries();
1886     if (!lastContentEntry) {
1887         contentEntries->reset(entry);
1888         setLastContentEntry(entry);
1889     } else if (xfermode == SkXfermode::kDstOver_Mode) {
1890         entry->fNext.reset(contentEntries->detach());
1891         contentEntries->reset(entry);
1892     } else {
1893         lastContentEntry->fNext.reset(entry);
1894         setLastContentEntry(entry);
1895     }
1896     newEntry.detach();
1897     return entry;
1898 }
1899 
finishContentEntry(SkXfermode::Mode xfermode,SkPDFFormXObject * dst,SkPath * shape)1900 void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
1901                                      SkPDFFormXObject* dst,
1902                                      SkPath* shape) {
1903     if (xfermode != SkXfermode::kClear_Mode       &&
1904             xfermode != SkXfermode::kSrc_Mode     &&
1905             xfermode != SkXfermode::kDstOver_Mode &&
1906             xfermode != SkXfermode::kSrcIn_Mode   &&
1907             xfermode != SkXfermode::kDstIn_Mode   &&
1908             xfermode != SkXfermode::kSrcOut_Mode  &&
1909             xfermode != SkXfermode::kDstOut_Mode  &&
1910             xfermode != SkXfermode::kSrcATop_Mode &&
1911             xfermode != SkXfermode::kDstATop_Mode &&
1912             xfermode != SkXfermode::kModulate_Mode) {
1913         SkASSERT(!dst);
1914         return;
1915     }
1916     if (xfermode == SkXfermode::kDstOver_Mode) {
1917         SkASSERT(!dst);
1918         ContentEntry* firstContentEntry = getContentEntries()->get();
1919         if (firstContentEntry->fContent.getOffset() == 0) {
1920             // For DstOver, an empty content entry was inserted before the rest
1921             // of the content entries. If nothing was drawn, it needs to be
1922             // removed.
1923             SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries();
1924             contentEntries->reset(firstContentEntry->fNext.detach());
1925         }
1926         return;
1927     }
1928     if (!dst) {
1929         SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
1930                  xfermode == SkXfermode::kSrcOut_Mode);
1931         return;
1932     }
1933 
1934     ContentEntry* contentEntries = getContentEntries()->get();
1935     SkASSERT(dst);
1936     SkASSERT(!contentEntries->fNext.get());
1937     // Changing the current content into a form-xobject will destroy the clip
1938     // objects which is fine since the xobject will already be clipped. However
1939     // if source has shape, we need to clip it too, so a copy of the clip is
1940     // saved.
1941     SkClipStack clipStack = contentEntries->fState.fClipStack;
1942     SkRegion clipRegion = contentEntries->fState.fClipRegion;
1943 
1944     SkMatrix identity;
1945     identity.reset();
1946     SkPaint stockPaint;
1947 
1948     SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
1949     if (isContentEmpty()) {
1950         // If nothing was drawn and there's no shape, then the draw was a
1951         // no-op, but dst needs to be restored for that to be true.
1952         // If there is shape, then an empty source with Src, SrcIn, SrcOut,
1953         // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
1954         // reduces to Dst.
1955         if (shape == nullptr || xfermode == SkXfermode::kDstOut_Mode ||
1956                 xfermode == SkXfermode::kSrcATop_Mode) {
1957             ScopedContentEntry content(this, &fExistingClipStack,
1958                                        fExistingClipRegion, identity,
1959                                        stockPaint);
1960             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
1961                                         &content.entry()->fContent);
1962             return;
1963         } else {
1964             xfermode = SkXfermode::kClear_Mode;
1965         }
1966     } else {
1967         SkASSERT(!fContentEntries->fNext.get());
1968         srcFormXObject.reset(createFormXObjectFromDevice());
1969     }
1970 
1971     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
1972     // without alpha.
1973     if (xfermode == SkXfermode::kSrcATop_Mode) {
1974         // TODO(vandebo): In order to properly support SrcATop we have to track
1975         // the shape of what's been drawn at all times. It's the intersection of
1976         // the non-transparent parts of the device and the outlines (shape) of
1977         // all images and devices drawn.
1978         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
1979                                 &fExistingClipStack, fExistingClipRegion,
1980                                 SkXfermode::kSrcOver_Mode, true);
1981     } else {
1982         SkAutoTUnref<SkPDFFormXObject> dstMaskStorage;
1983         SkPDFFormXObject* dstMask = srcFormXObject.get();
1984         if (shape != nullptr) {
1985             // Draw shape into a form-xobject.
1986             SkDraw d;
1987             d.fMatrix = &identity;
1988             d.fClip = &clipRegion;
1989             d.fClipStack = &clipStack;
1990             SkPaint filledPaint;
1991             filledPaint.setColor(SK_ColorBLACK);
1992             filledPaint.setStyle(SkPaint::kFill_Style);
1993             this->drawPath(d, *shape, filledPaint, nullptr, true);
1994 
1995             dstMaskStorage.reset(createFormXObjectFromDevice());
1996             dstMask = dstMaskStorage.get();
1997         }
1998         drawFormXObjectWithMask(addXObjectResource(dst), dstMask,
1999                                 &fExistingClipStack, fExistingClipRegion,
2000                                 SkXfermode::kSrcOver_Mode, true);
2001     }
2002 
2003     if (xfermode == SkXfermode::kClear_Mode) {
2004         return;
2005     } else if (xfermode == SkXfermode::kSrc_Mode ||
2006             xfermode == SkXfermode::kDstATop_Mode) {
2007         ScopedContentEntry content(this, &fExistingClipStack,
2008                                    fExistingClipRegion, identity, stockPaint);
2009         if (content.entry()) {
2010             SkPDFUtils::DrawFormXObject(
2011                     this->addXObjectResource(srcFormXObject.get()),
2012                     &content.entry()->fContent);
2013         }
2014         if (xfermode == SkXfermode::kSrc_Mode) {
2015             return;
2016         }
2017     } else if (xfermode == SkXfermode::kSrcATop_Mode) {
2018         ScopedContentEntry content(this, &fExistingClipStack,
2019                                    fExistingClipRegion, identity, stockPaint);
2020         if (content.entry()) {
2021             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
2022                                         &content.entry()->fContent);
2023         }
2024     }
2025 
2026     SkASSERT(xfermode == SkXfermode::kSrcIn_Mode   ||
2027              xfermode == SkXfermode::kDstIn_Mode   ||
2028              xfermode == SkXfermode::kSrcOut_Mode  ||
2029              xfermode == SkXfermode::kDstOut_Mode  ||
2030              xfermode == SkXfermode::kSrcATop_Mode ||
2031              xfermode == SkXfermode::kDstATop_Mode ||
2032              xfermode == SkXfermode::kModulate_Mode);
2033 
2034     if (xfermode == SkXfermode::kSrcIn_Mode ||
2035             xfermode == SkXfermode::kSrcOut_Mode ||
2036             xfermode == SkXfermode::kSrcATop_Mode) {
2037         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
2038                                 &fExistingClipStack, fExistingClipRegion,
2039                                 SkXfermode::kSrcOver_Mode,
2040                                 xfermode == SkXfermode::kSrcOut_Mode);
2041     } else {
2042         SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
2043         if (xfermode == SkXfermode::kModulate_Mode) {
2044             drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
2045                                     dst, &fExistingClipStack,
2046                                     fExistingClipRegion,
2047                                     SkXfermode::kSrcOver_Mode, false);
2048             mode = SkXfermode::kMultiply_Mode;
2049         }
2050         drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(),
2051                                 &fExistingClipStack, fExistingClipRegion, mode,
2052                                 xfermode == SkXfermode::kDstOut_Mode);
2053     }
2054 }
2055 
isContentEmpty()2056 bool SkPDFDevice::isContentEmpty() {
2057     ContentEntry* contentEntries = getContentEntries()->get();
2058     if (!contentEntries || contentEntries->fContent.getOffset() == 0) {
2059         SkASSERT(!contentEntries || !contentEntries->fNext.get());
2060         return true;
2061     }
2062     return false;
2063 }
2064 
populateGraphicStateEntryFromPaint(const SkMatrix & matrix,const SkClipStack & clipStack,const SkRegion & clipRegion,const SkPaint & paint,bool hasText,GraphicStateEntry * entry)2065 void SkPDFDevice::populateGraphicStateEntryFromPaint(
2066         const SkMatrix& matrix,
2067         const SkClipStack& clipStack,
2068         const SkRegion& clipRegion,
2069         const SkPaint& paint,
2070         bool hasText,
2071         GraphicStateEntry* entry) {
2072     NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
2073     NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
2074     NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
2075 
2076     entry->fMatrix = matrix;
2077     entry->fClipStack = clipStack;
2078     entry->fClipRegion = clipRegion;
2079     entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
2080     entry->fShaderIndex = -1;
2081 
2082     // PDF treats a shader as a color, so we only set one or the other.
2083     SkAutoTUnref<SkPDFObject> pdfShader;
2084     const SkShader* shader = paint.getShader();
2085     SkColor color = paint.getColor();
2086     if (shader) {
2087         // PDF positions patterns relative to the initial transform, so
2088         // we need to apply the current transform to the shader parameters.
2089         SkMatrix transform = matrix;
2090         transform.postConcat(fInitialTransform);
2091 
2092         // PDF doesn't support kClamp_TileMode, so we simulate it by making
2093         // a pattern the size of the current clip.
2094         SkIRect bounds = clipRegion.getBounds();
2095 
2096         // We need to apply the initial transform to bounds in order to get
2097         // bounds in a consistent coordinate system.
2098         SkRect boundsTemp;
2099         boundsTemp.set(bounds);
2100         fInitialTransform.mapRect(&boundsTemp);
2101         boundsTemp.roundOut(&bounds);
2102 
2103         SkScalar rasterScale =
2104                 SkIntToScalar(fRasterDpi) / DPI_FOR_RASTER_SCALE_ONE;
2105         pdfShader.reset(SkPDFShader::GetPDFShader(
2106                 fCanon, fRasterDpi, *shader, transform, bounds, rasterScale));
2107 
2108         if (pdfShader.get()) {
2109             // pdfShader has been canonicalized so we can directly compare
2110             // pointers.
2111             int resourceIndex = fShaderResources.find(pdfShader.get());
2112             if (resourceIndex < 0) {
2113                 resourceIndex = fShaderResources.count();
2114                 fShaderResources.push(pdfShader.get());
2115                 pdfShader.get()->ref();
2116             }
2117             entry->fShaderIndex = resourceIndex;
2118         } else {
2119             // A color shader is treated as an invalid shader so we don't have
2120             // to set a shader just for a color.
2121             SkShader::GradientInfo gradientInfo;
2122             SkColor gradientColor;
2123             gradientInfo.fColors = &gradientColor;
2124             gradientInfo.fColorOffsets = nullptr;
2125             gradientInfo.fColorCount = 1;
2126             if (shader->asAGradient(&gradientInfo) ==
2127                     SkShader::kColor_GradientType) {
2128                 entry->fColor = SkColorSetA(gradientColor, 0xFF);
2129                 color = gradientColor;
2130             }
2131         }
2132     }
2133 
2134     SkAutoTUnref<SkPDFGraphicState> newGraphicState;
2135     if (color == paint.getColor()) {
2136         newGraphicState.reset(
2137                 SkPDFGraphicState::GetGraphicStateForPaint(fCanon, paint));
2138     } else {
2139         SkPaint newPaint = paint;
2140         newPaint.setColor(color);
2141         newGraphicState.reset(
2142                 SkPDFGraphicState::GetGraphicStateForPaint(fCanon, newPaint));
2143     }
2144     int resourceIndex = addGraphicStateResource(newGraphicState.get());
2145     entry->fGraphicStateIndex = resourceIndex;
2146 
2147     if (hasText) {
2148         entry->fTextScaleX = paint.getTextScaleX();
2149         entry->fTextFill = paint.getStyle();
2150     } else {
2151         entry->fTextScaleX = 0;
2152     }
2153 }
2154 
addGraphicStateResource(SkPDFObject * gs)2155 int SkPDFDevice::addGraphicStateResource(SkPDFObject* gs) {
2156     // Assumes that gs has been canonicalized (so we can directly compare
2157     // pointers).
2158     int result = fGraphicStateResources.find(gs);
2159     if (result < 0) {
2160         result = fGraphicStateResources.count();
2161         fGraphicStateResources.push(gs);
2162         gs->ref();
2163     }
2164     return result;
2165 }
2166 
addXObjectResource(SkPDFObject * xObject)2167 int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
2168     // Assumes that xobject has been canonicalized (so we can directly compare
2169     // pointers).
2170     int result = fXObjectResources.find(xObject);
2171     if (result < 0) {
2172         result = fXObjectResources.count();
2173         fXObjectResources.push(xObject);
2174         xObject->ref();
2175     }
2176     return result;
2177 }
2178 
updateFont(const SkPaint & paint,uint16_t glyphID,ContentEntry * contentEntry)2179 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
2180                              ContentEntry* contentEntry) {
2181     SkTypeface* typeface = paint.getTypeface();
2182     if (contentEntry->fState.fFont == nullptr ||
2183             contentEntry->fState.fTextSize != paint.getTextSize() ||
2184             !contentEntry->fState.fFont->hasGlyph(glyphID)) {
2185         int fontIndex = getFontResourceIndex(typeface, glyphID);
2186         contentEntry->fContent.writeText("/");
2187         contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName(
2188                 SkPDFResourceDict::kFont_ResourceType,
2189                 fontIndex).c_str());
2190         contentEntry->fContent.writeText(" ");
2191         SkPDFUtils::AppendScalar(paint.getTextSize(), &contentEntry->fContent);
2192         contentEntry->fContent.writeText(" Tf\n");
2193         contentEntry->fState.fFont = fFontResources[fontIndex];
2194     }
2195 }
2196 
getFontResourceIndex(SkTypeface * typeface,uint16_t glyphID)2197 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
2198     SkAutoTUnref<SkPDFFont> newFont(
2199             SkPDFFont::GetFontResource(fCanon, typeface, glyphID));
2200     int resourceIndex = fFontResources.find(newFont.get());
2201     if (resourceIndex < 0) {
2202         resourceIndex = fFontResources.count();
2203         fFontResources.push(newFont.get());
2204         newFont.get()->ref();
2205     }
2206     return resourceIndex;
2207 }
2208 
rect_to_size(const SkRect & r)2209 static SkSize rect_to_size(const SkRect& r) {
2210     return SkSize::Make(r.width(), r.height());
2211 }
2212 
color_filter(const SkImage * image,SkColorFilter * colorFilter)2213 static const SkImage* color_filter(const SkImage* image,
2214                                    SkColorFilter* colorFilter) {
2215     SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(
2216             SkImageInfo::MakeN32Premul(image->dimensions())));
2217     if (!surface) {
2218         return image;
2219     }
2220     SkCanvas* canvas = surface->getCanvas();
2221     canvas->clear(SK_ColorTRANSPARENT);
2222     SkPaint paint;
2223     paint.setColorFilter(colorFilter);
2224     canvas->drawImage(image, 0, 0, &paint);
2225     canvas->flush();
2226     return surface->newImageSnapshot();
2227 }
2228 
2229 ////////////////////////////////////////////////////////////////////////////////
internalDrawImage(const SkMatrix & origMatrix,const SkClipStack * clipStack,const SkRegion & origClipRegion,const SkImage * image,const SkIRect * srcRect,const SkPaint & paint)2230 void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix,
2231                                     const SkClipStack* clipStack,
2232                                     const SkRegion& origClipRegion,
2233                                     const SkImage* image,
2234                                     const SkIRect* srcRect,
2235                                     const SkPaint& paint) {
2236     SkASSERT(image);
2237     #ifdef SK_PDF_IMAGE_STATS
2238     gDrawImageCalls.fetch_add(1);
2239     #endif
2240     SkMatrix matrix = origMatrix;
2241     SkRegion perspectiveBounds;
2242     const SkRegion* clipRegion = &origClipRegion;
2243     SkAutoTUnref<const SkImage> autoImageUnref;
2244 
2245     if (srcRect) {
2246         autoImageUnref.reset(image->newSubset(*srcRect));
2247         if (!autoImageUnref) {
2248             return;
2249         }
2250         image = autoImageUnref;
2251     }
2252     // Rasterize the bitmap using perspective in a new bitmap.
2253     if (origMatrix.hasPerspective()) {
2254         if (fRasterDpi == 0) {
2255             return;
2256         }
2257         // Transform the bitmap in the new space, without taking into
2258         // account the initial transform.
2259         SkPath perspectiveOutline;
2260         SkRect imageBounds = SkRect::Make(image->bounds());
2261         perspectiveOutline.addRect(imageBounds);
2262         perspectiveOutline.transform(origMatrix);
2263 
2264         // TODO(edisonn): perf - use current clip too.
2265         // Retrieve the bounds of the new shape.
2266         SkRect bounds = perspectiveOutline.getBounds();
2267 
2268         // Transform the bitmap in the new space, taking into
2269         // account the initial transform.
2270         SkMatrix total = origMatrix;
2271         total.postConcat(fInitialTransform);
2272         SkScalar dpiScale = SkIntToScalar(fRasterDpi) /
2273                             SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE);
2274         total.postScale(dpiScale, dpiScale);
2275 
2276         SkPath physicalPerspectiveOutline;
2277         physicalPerspectiveOutline.addRect(imageBounds);
2278         physicalPerspectiveOutline.transform(total);
2279 
2280         SkRect physicalPerspectiveBounds =
2281                 physicalPerspectiveOutline.getBounds();
2282         SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width();
2283         SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height();
2284 
2285         // TODO(edisonn): A better approach would be to use a bitmap shader
2286         // (in clamp mode) and draw a rect over the entire bounding box. Then
2287         // intersect perspectiveOutline to the clip. That will avoid introducing
2288         // alpha to the image while still giving good behavior at the edge of
2289         // the image.  Avoiding alpha will reduce the pdf size and generation
2290         // CPU time some.
2291 
2292         SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil();
2293 
2294         SkAutoTUnref<SkSurface> surface(
2295                 SkSurface::NewRaster(SkImageInfo::MakeN32Premul(wh)));
2296         if (!surface) {
2297             return;
2298         }
2299         SkCanvas* canvas = surface->getCanvas();
2300         canvas->clear(SK_ColorTRANSPARENT);
2301 
2302         SkScalar deltaX = bounds.left();
2303         SkScalar deltaY = bounds.top();
2304 
2305         SkMatrix offsetMatrix = origMatrix;
2306         offsetMatrix.postTranslate(-deltaX, -deltaY);
2307         offsetMatrix.postScale(scaleX, scaleY);
2308 
2309         // Translate the draw in the new canvas, so we perfectly fit the
2310         // shape in the bitmap.
2311         canvas->setMatrix(offsetMatrix);
2312         canvas->drawImage(image, 0, 0, nullptr);
2313         // Make sure the final bits are in the bitmap.
2314         canvas->flush();
2315 
2316         // In the new space, we use the identity matrix translated
2317         // and scaled to reflect DPI.
2318         matrix.setScale(1 / scaleX, 1 / scaleY);
2319         matrix.postTranslate(deltaX, deltaY);
2320 
2321         perspectiveBounds.setRect(bounds.roundOut());
2322         clipRegion = &perspectiveBounds;
2323         srcRect = nullptr;
2324 
2325         autoImageUnref.reset(surface->newImageSnapshot());
2326         image = autoImageUnref;
2327     }
2328 
2329     SkMatrix scaled;
2330     // Adjust for origin flip.
2331     scaled.setScale(SK_Scalar1, -SK_Scalar1);
2332     scaled.postTranslate(0, SK_Scalar1);
2333     // Scale the image up from 1x1 to WxH.
2334     SkIRect subset = image->bounds();
2335     scaled.postScale(SkIntToScalar(image->width()),
2336                      SkIntToScalar(image->height()));
2337     scaled.postConcat(matrix);
2338     ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint);
2339     if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) {
2340         return;
2341     }
2342     if (content.needShape()) {
2343         SkPath shape;
2344         shape.addRect(SkRect::Make(subset));
2345         shape.transform(matrix);
2346         content.setShape(shape);
2347     }
2348     if (!content.needSource()) {
2349         return;
2350     }
2351 
2352     if (SkColorFilter* colorFilter = paint.getColorFilter()) {
2353         // TODO(https://bug.skia.org/4378): implement colorfilter on other
2354         // draw calls.  This code here works for all
2355         // drawBitmap*()/drawImage*() calls amd ImageFilters (which
2356         // rasterize a layer on this backend).  Fortuanely, this seems
2357         // to be how Chromium impements most color-filters.
2358         autoImageUnref.reset(color_filter(image, colorFilter));
2359         image = autoImageUnref;
2360         // TODO(halcanary): de-dupe this by caching filtered images.
2361         // (maybe in the resource cache?)
2362     }
2363     SkAutoTUnref<SkPDFObject> pdfimage(SkSafeRef(fCanon->findPDFBitmap(image)));
2364     if (!pdfimage) {
2365         pdfimage.reset(SkPDFCreateBitmapObject(
2366                                image, fCanon->fPixelSerializer));
2367         if (!pdfimage) {
2368             return;
2369         }
2370         fCanon->addPDFBitmap(image->uniqueID(), pdfimage);
2371     }
2372     SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()),
2373                                 &content.entry()->fContent);
2374 }
2375