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 9 #include "SkData.h" 10 #include "SkGeometry.h" 11 #include "SkPaint.h" 12 #include "SkPath.h" 13 #include "SkPDFResourceDict.h" 14 #include "SkPDFUtils.h" 15 #include "SkStream.h" 16 #include "SkString.h" 17 #include "SkPDFTypes.h" 18 19 //static 20 SkPDFArray* SkPDFUtils::RectToArray(const SkRect& rect) { 21 SkPDFArray* result = new SkPDFArray(); 22 result->reserve(4); 23 result->appendScalar(rect.fLeft); 24 result->appendScalar(rect.fTop); 25 result->appendScalar(rect.fRight); 26 result->appendScalar(rect.fBottom); 27 return result; 28 } 29 30 // static 31 SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) { 32 SkScalar values[6]; 33 if (!matrix.asAffine(values)) { 34 SkMatrix::SetAffineIdentity(values); 35 } 36 37 SkPDFArray* result = new SkPDFArray; 38 result->reserve(6); 39 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { 40 result->appendScalar(values[i]); 41 } 42 return result; 43 } 44 45 // static 46 void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) { 47 SkScalar values[6]; 48 if (!matrix.asAffine(values)) { 49 SkMatrix::SetAffineIdentity(values); 50 } 51 for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { 52 SkPDFUtils::AppendScalar(values[i], content); 53 content->writeText(" "); 54 } 55 content->writeText("cm\n"); 56 } 57 58 // static 59 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) { 60 SkPDFUtils::AppendScalar(x, content); 61 content->writeText(" "); 62 SkPDFUtils::AppendScalar(y, content); 63 content->writeText(" m\n"); 64 } 65 66 // static 67 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) { 68 SkPDFUtils::AppendScalar(x, content); 69 content->writeText(" "); 70 SkPDFUtils::AppendScalar(y, content); 71 content->writeText(" l\n"); 72 } 73 74 // static 75 void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y, 76 SkScalar ctl2X, SkScalar ctl2Y, 77 SkScalar dstX, SkScalar dstY, SkWStream* content) { 78 SkString cmd("y\n"); 79 SkPDFUtils::AppendScalar(ctl1X, content); 80 content->writeText(" "); 81 SkPDFUtils::AppendScalar(ctl1Y, content); 82 content->writeText(" "); 83 if (ctl2X != dstX || ctl2Y != dstY) { 84 cmd.set("c\n"); 85 SkPDFUtils::AppendScalar(ctl2X, content); 86 content->writeText(" "); 87 SkPDFUtils::AppendScalar(ctl2Y, content); 88 content->writeText(" "); 89 } 90 SkPDFUtils::AppendScalar(dstX, content); 91 content->writeText(" "); 92 SkPDFUtils::AppendScalar(dstY, content); 93 content->writeText(" "); 94 content->writeText(cmd.c_str()); 95 } 96 97 static void append_quad(const SkPoint quad[], SkWStream* content) { 98 SkPoint cubic[4]; 99 SkConvertQuadToCubic(quad, cubic); 100 SkPDFUtils::AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, 101 cubic[3].fX, cubic[3].fY, content); 102 } 103 104 // static 105 void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) { 106 // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. 107 SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop); 108 109 SkPDFUtils::AppendScalar(rect.fLeft, content); 110 content->writeText(" "); 111 SkPDFUtils::AppendScalar(bottom, content); 112 content->writeText(" "); 113 SkPDFUtils::AppendScalar(rect.width(), content); 114 content->writeText(" "); 115 SkPDFUtils::AppendScalar(rect.height(), content); 116 content->writeText(" re\n"); 117 } 118 119 // static 120 void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, 121 SkWStream* content) { 122 // Filling a path with no area results in a drawing in PDF renderers but 123 // Chrome expects to be able to draw some such entities with no visible 124 // result, so we detect those cases and discard the drawing for them. 125 // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y). 126 enum SkipFillState { 127 kEmpty_SkipFillState = 0, 128 kSingleLine_SkipFillState = 1, 129 kNonSingleLine_SkipFillState = 2, 130 }; 131 SkipFillState fillState = kEmpty_SkipFillState; 132 if (paintStyle != SkPaint::kFill_Style) { 133 fillState = kNonSingleLine_SkipFillState; 134 } 135 SkPoint lastMovePt = SkPoint::Make(0,0); 136 SkDynamicMemoryWStream currentSegment; 137 SkPoint args[4]; 138 SkPath::Iter iter(path, false); 139 for (SkPath::Verb verb = iter.next(args); verb != SkPath::kDone_Verb; verb = iter.next(args)) { 140 // args gets all the points, even the implicit first point. 141 switch (verb) { 142 case SkPath::kMove_Verb: 143 MoveTo(args[0].fX, args[0].fY, ¤tSegment); 144 lastMovePt = args[0]; 145 fillState = kEmpty_SkipFillState; 146 break; 147 case SkPath::kLine_Verb: 148 AppendLine(args[1].fX, args[1].fY, ¤tSegment); 149 if (fillState == kEmpty_SkipFillState) { 150 if (args[0] != lastMovePt) { 151 fillState = kSingleLine_SkipFillState; 152 } 153 } else if (fillState == kSingleLine_SkipFillState) { 154 fillState = kNonSingleLine_SkipFillState; 155 } 156 break; 157 case SkPath::kQuad_Verb: 158 append_quad(args, ¤tSegment); 159 fillState = kNonSingleLine_SkipFillState; 160 break; 161 case SkPath::kConic_Verb: { 162 const SkScalar tol = SK_Scalar1 / 4; 163 SkAutoConicToQuads converter; 164 const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tol); 165 for (int i = 0; i < converter.countQuads(); ++i) { 166 append_quad(&quads[i * 2], ¤tSegment); 167 } 168 } break; 169 case SkPath::kCubic_Verb: 170 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, 171 args[3].fX, args[3].fY, ¤tSegment); 172 fillState = kNonSingleLine_SkipFillState; 173 break; 174 case SkPath::kClose_Verb: 175 if (fillState != kSingleLine_SkipFillState) { 176 ClosePath(¤tSegment); 177 currentSegment.writeToStream(content); 178 } 179 currentSegment.reset(); 180 break; 181 default: 182 SkASSERT(false); 183 break; 184 } 185 } 186 if (currentSegment.bytesWritten() > 0) { 187 currentSegment.writeToStream(content); 188 } 189 } 190 191 // static 192 void SkPDFUtils::ClosePath(SkWStream* content) { 193 content->writeText("h\n"); 194 } 195 196 // static 197 void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill, 198 SkWStream* content) { 199 if (style == SkPaint::kFill_Style) { 200 content->writeText("f"); 201 } else if (style == SkPaint::kStrokeAndFill_Style) { 202 content->writeText("B"); 203 } else if (style == SkPaint::kStroke_Style) { 204 content->writeText("S"); 205 } 206 207 if (style != SkPaint::kStroke_Style) { 208 NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false); 209 NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false); 210 if (fill == SkPath::kEvenOdd_FillType) { 211 content->writeText("*"); 212 } 213 } 214 content->writeText("\n"); 215 } 216 217 // static 218 void SkPDFUtils::StrokePath(SkWStream* content) { 219 SkPDFUtils::PaintPath( 220 SkPaint::kStroke_Style, SkPath::kWinding_FillType, content); 221 } 222 223 // static 224 void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) { 225 content->writeText("/"); 226 content->writeText(SkPDFResourceDict::getResourceName( 227 SkPDFResourceDict::kXObject_ResourceType, 228 objectIndex).c_str()); 229 content->writeText(" Do\n"); 230 } 231 232 // static 233 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) { 234 content->writeText("/"); 235 content->writeText(SkPDFResourceDict::getResourceName( 236 SkPDFResourceDict::kExtGState_ResourceType, 237 objectIndex).c_str()); 238 content->writeText(" gs\n"); 239 } 240 241 // static 242 void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) { 243 // Select Pattern color space (CS, cs) and set pattern object as current 244 // color (SCN, scn) 245 SkString resourceName = SkPDFResourceDict::getResourceName( 246 SkPDFResourceDict::kPattern_ResourceType, 247 objectIndex); 248 content->writeText("/Pattern CS/Pattern cs/"); 249 content->writeText(resourceName.c_str()); 250 content->writeText(" SCN/"); 251 content->writeText(resourceName.c_str()); 252 content->writeText(" scn\n"); 253 } 254 255 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) { 256 // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and 257 // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). 258 // When using floats that are outside the whole value range, we can use 259 // integers instead. 260 261 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) 262 if (value > 32767 || value < -32767) { 263 stream->writeDecAsText(SkScalarRoundToInt(value)); 264 return; 265 } 266 267 char buffer[SkStrAppendScalar_MaxSize]; 268 char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); 269 stream->write(buffer, end - buffer); 270 return; 271 #endif // !SK_ALLOW_LARGE_PDF_SCALARS 272 273 #if defined(SK_ALLOW_LARGE_PDF_SCALARS) 274 // Floats have 24bits of significance, so anything outside that range is 275 // no more precise than an int. (Plus PDF doesn't support scientific 276 // notation, so this clamps to SK_Max/MinS32). 277 if (value > (1 << 24) || value < -(1 << 24)) { 278 stream->writeDecAsText(value); 279 return; 280 } 281 // Continue to enforce the PDF limits for small floats. 282 if (value < 1.0f/65536 && value > -1.0f/65536) { 283 stream->writeDecAsText(0); 284 return; 285 } 286 // SkStrAppendFloat might still use scientific notation, so use snprintf 287 // directly.. 288 static const int kFloat_MaxSize = 19; 289 char buffer[kFloat_MaxSize]; 290 int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value); 291 // %f always prints trailing 0s, so strip them. 292 for (; buffer[len - 1] == '0' && len > 0; len--) { 293 buffer[len - 1] = '\0'; 294 } 295 if (buffer[len - 1] == '.') { 296 buffer[len - 1] = '\0'; 297 } 298 stream->writeText(buffer); 299 return; 300 #endif // SK_ALLOW_LARGE_PDF_SCALARS 301 } 302 303 SkString SkPDFUtils::FormatString(const char* cin, size_t len) { 304 SkDEBUGCODE(static const size_t kMaxLen = 65535;) 305 SkASSERT(len <= kMaxLen); 306 307 // 7-bit clean is a heuristic to decide what string format to use; 308 // a 7-bit clean string should require little escaping. 309 bool sevenBitClean = true; 310 size_t characterCount = 2 + len; 311 for (size_t i = 0; i < len; i++) { 312 if (cin[i] > '~' || cin[i] < ' ') { 313 sevenBitClean = false; 314 break; 315 } 316 if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') { 317 ++characterCount; 318 } 319 } 320 SkString result; 321 if (sevenBitClean) { 322 result.resize(characterCount); 323 char* str = result.writable_str(); 324 *str++ = '('; 325 for (size_t i = 0; i < len; i++) { 326 if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') { 327 *str++ = '\\'; 328 } 329 *str++ = cin[i]; 330 } 331 *str++ = ')'; 332 } else { 333 result.resize(2 * len + 2); 334 char* str = result.writable_str(); 335 *str++ = '<'; 336 for (size_t i = 0; i < len; i++) { 337 uint8_t c = static_cast<uint8_t>(cin[i]); 338 static const char gHex[] = "0123456789ABCDEF"; 339 *str++ = gHex[(c >> 4) & 0xF]; 340 *str++ = gHex[(c ) & 0xF]; 341 } 342 *str++ = '>'; 343 } 344 return result; 345 } 346