1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/pdf/SkPDFUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,249 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2011 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkData.h" 1.14 +#include "SkGeometry.h" 1.15 +#include "SkPaint.h" 1.16 +#include "SkPath.h" 1.17 +#include "SkPDFResourceDict.h" 1.18 +#include "SkPDFUtils.h" 1.19 +#include "SkStream.h" 1.20 +#include "SkString.h" 1.21 +#include "SkPDFTypes.h" 1.22 + 1.23 +//static 1.24 +SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) { 1.25 + SkPDFArray* result = new SkPDFArray(); 1.26 + result->reserve(4); 1.27 + result->appendScalar(rect.fLeft); 1.28 + result->appendScalar(rect.fTop); 1.29 + result->appendScalar(rect.fRight); 1.30 + result->appendScalar(rect.fBottom); 1.31 + return result; 1.32 +} 1.33 + 1.34 +// static 1.35 +SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) { 1.36 + SkScalar values[6]; 1.37 + if (!matrix.asAffine(values)) { 1.38 + SkMatrix::SetAffineIdentity(values); 1.39 + } 1.40 + 1.41 + SkPDFArray* result = new SkPDFArray; 1.42 + result->reserve(6); 1.43 + for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { 1.44 + result->appendScalar(values[i]); 1.45 + } 1.46 + return result; 1.47 +} 1.48 + 1.49 +// static 1.50 +void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) { 1.51 + SkScalar values[6]; 1.52 + if (!matrix.asAffine(values)) { 1.53 + SkMatrix::SetAffineIdentity(values); 1.54 + } 1.55 + for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { 1.56 + SkPDFScalar::Append(values[i], content); 1.57 + content->writeText(" "); 1.58 + } 1.59 + content->writeText("cm\n"); 1.60 +} 1.61 + 1.62 +// static 1.63 +void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) { 1.64 + SkPDFScalar::Append(x, content); 1.65 + content->writeText(" "); 1.66 + SkPDFScalar::Append(y, content); 1.67 + content->writeText(" m\n"); 1.68 +} 1.69 + 1.70 +// static 1.71 +void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) { 1.72 + SkPDFScalar::Append(x, content); 1.73 + content->writeText(" "); 1.74 + SkPDFScalar::Append(y, content); 1.75 + content->writeText(" l\n"); 1.76 +} 1.77 + 1.78 +// static 1.79 +void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y, 1.80 + SkScalar ctl2X, SkScalar ctl2Y, 1.81 + SkScalar dstX, SkScalar dstY, SkWStream* content) { 1.82 + SkString cmd("y\n"); 1.83 + SkPDFScalar::Append(ctl1X, content); 1.84 + content->writeText(" "); 1.85 + SkPDFScalar::Append(ctl1Y, content); 1.86 + content->writeText(" "); 1.87 + if (ctl2X != dstX || ctl2Y != dstY) { 1.88 + cmd.set("c\n"); 1.89 + SkPDFScalar::Append(ctl2X, content); 1.90 + content->writeText(" "); 1.91 + SkPDFScalar::Append(ctl2Y, content); 1.92 + content->writeText(" "); 1.93 + } 1.94 + SkPDFScalar::Append(dstX, content); 1.95 + content->writeText(" "); 1.96 + SkPDFScalar::Append(dstY, content); 1.97 + content->writeText(" "); 1.98 + content->writeText(cmd.c_str()); 1.99 +} 1.100 + 1.101 +// static 1.102 +void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) { 1.103 + // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. 1.104 + SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop); 1.105 + 1.106 + SkPDFScalar::Append(rect.fLeft, content); 1.107 + content->writeText(" "); 1.108 + SkPDFScalar::Append(bottom, content); 1.109 + content->writeText(" "); 1.110 + SkPDFScalar::Append(rect.width(), content); 1.111 + content->writeText(" "); 1.112 + SkPDFScalar::Append(rect.height(), content); 1.113 + content->writeText(" re\n"); 1.114 +} 1.115 + 1.116 +// static 1.117 +void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, 1.118 + SkWStream* content) { 1.119 + // Filling a path with no area results in a drawing in PDF renderers but 1.120 + // Chrome expects to be able to draw some such entities with no visible 1.121 + // result, so we detect those cases and discard the drawing for them. 1.122 + // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y). 1.123 + enum SkipFillState { 1.124 + kEmpty_SkipFillState = 0, 1.125 + kSingleLine_SkipFillState = 1, 1.126 + kNonSingleLine_SkipFillState = 2, 1.127 + }; 1.128 + SkipFillState fillState = kEmpty_SkipFillState; 1.129 + if (paintStyle != SkPaint::kFill_Style) { 1.130 + fillState = kNonSingleLine_SkipFillState; 1.131 + } 1.132 + SkPoint lastMovePt = SkPoint::Make(0,0); 1.133 + SkDynamicMemoryWStream currentSegment; 1.134 + SkPoint args[4]; 1.135 + SkPath::Iter iter(path, false); 1.136 + for (SkPath::Verb verb = iter.next(args); 1.137 + verb != SkPath::kDone_Verb; 1.138 + verb = iter.next(args)) { 1.139 + // args gets all the points, even the implicit first point. 1.140 + switch (verb) { 1.141 + case SkPath::kMove_Verb: 1.142 + MoveTo(args[0].fX, args[0].fY, ¤tSegment); 1.143 + lastMovePt = args[0]; 1.144 + fillState = kEmpty_SkipFillState; 1.145 + break; 1.146 + case SkPath::kLine_Verb: 1.147 + AppendLine(args[1].fX, args[1].fY, ¤tSegment); 1.148 + if (fillState == kEmpty_SkipFillState) { 1.149 + if (args[0] != lastMovePt) { 1.150 + fillState = kSingleLine_SkipFillState; 1.151 + } 1.152 + } else if (fillState == kSingleLine_SkipFillState) { 1.153 + fillState = kNonSingleLine_SkipFillState; 1.154 + } 1.155 + break; 1.156 + case SkPath::kQuad_Verb: { 1.157 + SkPoint cubic[4]; 1.158 + SkConvertQuadToCubic(args, cubic); 1.159 + AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, 1.160 + cubic[3].fX, cubic[3].fY, ¤tSegment); 1.161 + fillState = kNonSingleLine_SkipFillState; 1.162 + break; 1.163 + } 1.164 + case SkPath::kCubic_Verb: 1.165 + AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, 1.166 + args[3].fX, args[3].fY, ¤tSegment); 1.167 + fillState = kNonSingleLine_SkipFillState; 1.168 + break; 1.169 + case SkPath::kClose_Verb: 1.170 + if (fillState != kSingleLine_SkipFillState) { 1.171 + ClosePath(¤tSegment); 1.172 + SkData* data = currentSegment.copyToData(); 1.173 + content->write(data->data(), data->size()); 1.174 + data->unref(); 1.175 + } 1.176 + currentSegment.reset(); 1.177 + break; 1.178 + default: 1.179 + SkASSERT(false); 1.180 + break; 1.181 + } 1.182 + } 1.183 + if (currentSegment.bytesWritten() > 0) { 1.184 + SkData* data = currentSegment.copyToData(); 1.185 + content->write(data->data(), data->size()); 1.186 + data->unref(); 1.187 + } 1.188 +} 1.189 + 1.190 +// static 1.191 +void SkPDFUtils::ClosePath(SkWStream* content) { 1.192 + content->writeText("h\n"); 1.193 +} 1.194 + 1.195 +// static 1.196 +void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill, 1.197 + SkWStream* content) { 1.198 + if (style == SkPaint::kFill_Style) { 1.199 + content->writeText("f"); 1.200 + } else if (style == SkPaint::kStrokeAndFill_Style) { 1.201 + content->writeText("B"); 1.202 + } else if (style == SkPaint::kStroke_Style) { 1.203 + content->writeText("S"); 1.204 + } 1.205 + 1.206 + if (style != SkPaint::kStroke_Style) { 1.207 + NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false); 1.208 + NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false); 1.209 + if (fill == SkPath::kEvenOdd_FillType) { 1.210 + content->writeText("*"); 1.211 + } 1.212 + } 1.213 + content->writeText("\n"); 1.214 +} 1.215 + 1.216 +// static 1.217 +void SkPDFUtils::StrokePath(SkWStream* content) { 1.218 + SkPDFUtils::PaintPath( 1.219 + SkPaint::kStroke_Style, SkPath::kWinding_FillType, content); 1.220 +} 1.221 + 1.222 +// static 1.223 +void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) { 1.224 + content->writeText("/"); 1.225 + content->writeText(SkPDFResourceDict::getResourceName( 1.226 + SkPDFResourceDict::kXObject_ResourceType, 1.227 + objectIndex).c_str()); 1.228 + content->writeText(" Do\n"); 1.229 +} 1.230 + 1.231 +// static 1.232 +void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) { 1.233 + content->writeText("/"); 1.234 + content->writeText(SkPDFResourceDict::getResourceName( 1.235 + SkPDFResourceDict::kExtGState_ResourceType, 1.236 + objectIndex).c_str()); 1.237 + content->writeText(" gs\n"); 1.238 +} 1.239 + 1.240 +// static 1.241 +void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) { 1.242 + // Select Pattern color space (CS, cs) and set pattern object as current 1.243 + // color (SCN, scn) 1.244 + SkString resourceName = SkPDFResourceDict::getResourceName( 1.245 + SkPDFResourceDict::kPattern_ResourceType, 1.246 + objectIndex); 1.247 + content->writeText("/Pattern CS/Pattern cs/"); 1.248 + content->writeText(resourceName.c_str()); 1.249 + content->writeText(" SCN/"); 1.250 + content->writeText(resourceName.c_str()); 1.251 + content->writeText(" scn\n"); 1.252 +}