aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-10-20 22:23:29 +0000
committerGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-10-20 22:23:29 +0000
commit9b49dc0db8254e3dcdc2de4a1e0add4f8a7ac5a8 (patch)
tree35491ac7fd0c41cb8b5fb0a06a24840615e75b3a
parent17f694b0386ae42e66e76ffa19a0dabbc2191c62 (diff)
First pieces of SkPDFDevice. Supports:
Matrix transforms. Rendering bitmaps. Basic paint parameters. Rendering rectangles, points, lines, polygons. Render a paint to the page. Review URL: http://codereview.appspot.com/2584041 git-svn-id: http://skia.googlecode.com/svn/trunk@614 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkDevice.h4
-rw-r--r--include/pdf/SkPDFDevice.h155
-rw-r--r--include/pdf/SkPDFGraphicState.h80
-rw-r--r--include/pdf/SkPDFImage.h67
-rw-r--r--include/pdf/SkPDFTypes.h21
-rw-r--r--src/pdf/SkPDFDevice.cpp493
-rw-r--r--src/pdf/SkPDFGraphicState.cpp163
-rw-r--r--src/pdf/SkPDFImage.cpp248
-rw-r--r--src/pdf/SkPDFStream.cpp4
-rw-r--r--src/pdf/SkPDFTypes.cpp20
-rw-r--r--src/pdf/pdf_files.mk3
-rw-r--r--tests/PDFPrimitivesTest.cpp4
12 files changed, 1256 insertions, 6 deletions
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index c223a157a6..3932ad35c5 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -70,10 +70,10 @@ public:
/** Return the width of the device (in pixels).
*/
- int width() const { return fBitmap.width(); }
+ virtual int width() const { return fBitmap.width(); }
/** Return the height of the device (in pixels).
*/
- int height() const { return fBitmap.height(); }
+ virtual int height() const { return fBitmap.height(); }
/** Return the bitmap config of the device's pixels
*/
SkBitmap::Config config() const { return fBitmap.getConfig(); }
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
new file mode 100644
index 0000000000..5421299226
--- /dev/null
+++ b/include/pdf/SkPDFDevice.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFDevice_DEFINED
+#define SkPDFDevice_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkDevice.h"
+#include "SkString.h"
+
+class SkPDFArray;
+class SkPDFDevice;
+class SkPDFDict;
+class SkPDFGraphicState;
+class SkPDFObject;
+class SkPDFStream;
+
+class SkPDFDeviceFactory : public SkDeviceFactory {
+ virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height,
+ bool isOpaque, bool isForLayer);
+ virtual uint32_t getDeviceCapabilities() { return 0; }
+};
+
+/** \class SkPDFDevice
+
+ The drawing context for the PDF backend.
+*/
+class SkPDFDevice : public SkDevice {
+public:
+ /** Create a PDF drawing context with the given width and height.
+ * 72 points/in means letter paper is 612x792.
+ * @param width Page width in points.
+ * @param height Page height in points.
+ */
+ SkPDFDevice(int width, int height);
+ virtual ~SkPDFDevice();
+
+ virtual SkDeviceFactory* getDeviceFactory() {
+ return SkNEW(SkPDFDeviceFactory);
+ }
+
+ virtual int width() const { return fWidth; };
+
+ virtual int height() const { return fHeight; };
+
+ /** Called with the correct matrix and clip before this device is drawn
+ to using those settings. If your subclass overrides this, be sure to
+ call through to the base class as well.
+ */
+ virtual void setMatrixClip(const SkMatrix&, const SkRegion&);
+
+ /** These are called inside the per-device-layer loop for each draw call.
+ When these are called, we have already applied any saveLayer operations,
+ and are handling any looping from the paint, and any effects from the
+ DrawFilter.
+ */
+ virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+ virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+ size_t count, const SkPoint[],
+ const SkPaint& paint);
+ virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
+ virtual void drawPath(const SkDraw&, const SkPath& path,
+ const SkPaint& paint);
+ virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint);
+ virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint);
+ virtual void drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint);
+ virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint);
+ virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint);
+ virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
+ int vertexCount, const SkPoint verts[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode, const uint16_t indices[],
+ int indexCount, const SkPaint& paint);
+ virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&);
+
+ // PDF specific methods.
+
+ /** Returns a reference to the resource dictionary for this device.
+ */
+ const SkRefPtr<SkPDFDict>& getResourceDict();
+
+ /** Get the list of resouces (PDF objects) used on this page
+ * @param resouceList A list to append the resouces to.
+ */
+ void getResouces(SkTDArray<SkPDFObject*>* resouceList);
+
+ /** Returns the media box for this device.
+ */
+ SkRefPtr<SkPDFArray> getMediaBox();
+
+ /** Returns a string with the page contents.
+ */
+ SkString content();
+
+private:
+ int fWidth;
+ int fHeight;
+ SkRefPtr<SkPDFDict> fResourceDict;
+
+ SkRefPtr<SkPDFGraphicState> fCurrentGraphicState;
+ SkColor fCurrentColor;
+ SkScalar fCurrentTextScaleX;
+ SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
+ SkTDArray<SkPDFObject*> fXObjectResources;
+
+ SkString fContent;
+
+ // The last requested transforms from SkCanvas (setMatrixClip)
+ SkMatrix fCurTransform;
+
+ // The transform currently in effect in the PDF content stream.
+ SkMatrix fActiveTransform;
+
+ void updateGSFromPaint(const SkPaint& newPaint, SkString* textStaetUpdate);
+
+ void moveTo(SkScalar x, SkScalar y);
+ void appendLine(SkScalar x, SkScalar y);
+ void appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+ SkScalar ctl2X, SkScalar ctl2Y,
+ SkScalar dstX, SkScalar dstY);
+ void appendRectangle(SkScalar x, SkScalar y, SkScalar w, SkScalar h);
+ void closePath();
+ void strokePath();
+ void internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap,
+ const SkPaint& paint);
+
+ void setTransform(const SkMatrix& matrix);
+ void setNoTransform();
+ void applyTempTransform(const SkMatrix& matrix);
+ void removeTempTransform();
+ void applyTransform(const SkMatrix& matrix);
+};
+
+#endif
diff --git a/include/pdf/SkPDFGraphicState.h b/include/pdf/SkPDFGraphicState.h
new file mode 100644
index 0000000000..7fb09d46e0
--- /dev/null
+++ b/include/pdf/SkPDFGraphicState.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFGraphicState_DEFINED
+#define SkPDFGraphicState_DEFINED
+
+#include "SkPaint.h"
+#include "SkPDFTypes.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+/** \class SkPDFGraphicState
+ SkPaint objects roughly correspond to graphic state dictionaries that can
+ be installed. So that a given dictionary is only output to the pdf file
+ once, we want to canonicalize them. Static methods in this class manage
+ a weakly referenced set of SkPDFGraphicState objects: when the last
+ reference to a SkPDFGraphicState is removed, it removes itself from the
+ static set of objects.
+
+*/
+class SkPDFGraphicState : public SkPDFDict {
+public:
+ virtual ~SkPDFGraphicState();
+
+ // Override emitObject and getOutputSize so that we can populate
+ // the dictionary on demand.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** Get the graphic state for the passed SkPaint. The reference count of
+ * the object is incremented and it is the caller's responsibility to
+ * unreference it when done. This is needed to accommodate the weak
+ * reference pattern used when the returned object is new and has no
+ * other references.
+ * @param paint The SkPaint to emulate.
+ */
+ static SkPDFGraphicState* getGraphicStateForPaint(const SkPaint& paint);
+
+private:
+ const SkPaint fPaint;
+ bool fPopulated;
+
+ class GSCanonicalEntry {
+ public:
+ SkPDFGraphicState* fGraphicState;
+ const SkPaint* fPaint;
+
+ bool operator==(const GSCanonicalEntry& b) const;
+ explicit GSCanonicalEntry(SkPDFGraphicState* gs)
+ : fGraphicState(gs),
+ fPaint(&gs->fPaint) {}
+ explicit GSCanonicalEntry(const SkPaint* paint) : fPaint(paint) {}
+ };
+
+ // This should be made a hash table if performance is a problem.
+ static SkTDArray<GSCanonicalEntry>& canonicalPaints();
+ static SkMutex& canonicalPaintsMutex();
+
+ explicit SkPDFGraphicState(const SkPaint& paint);
+
+ void populateDict();
+
+ static int find(const SkPaint& paint);
+};
+
+#endif
diff --git a/include/pdf/SkPDFImage.h b/include/pdf/SkPDFImage.h
new file mode 100644
index 0000000000..6e5ee37926
--- /dev/null
+++ b/include/pdf/SkPDFImage.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFImage_DEFINED
+#define SkPDFImage_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPDFCatalog;
+
+/** \class SkPDFImage
+
+ An image XObject.
+*/
+
+// We could play the same trick here as is done in SkPDFGraphicState, storing
+// a copy of the Bitmap object (not the pixels), the pixel generation number,
+// and settings used from the paint to canonicalize image objects.
+class SkPDFImage : public SkPDFObject {
+public:
+ /** Create a PDF image XObject. Entries for the image properties are
+ * automatically added to the stream dictionary.
+ * @param bitmap The image to use.
+ * @param paint Used to calculate alpha, masks, etc.
+ */
+ SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint);
+ virtual ~SkPDFImage();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** Add the value to the stream dictionary with the given key.
+ * @param key The key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ */
+ void insert(SkPDFName* key, SkPDFObject* value);
+
+ /** Add the value to the stream dictionary with the given key.
+ * @param key The text of the key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ */
+ void insert(const char key[], SkPDFObject* value);
+
+private:
+ SkRefPtr<SkPDFStream> fStream;
+};
+
+#endif
diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h
index e0b1041b92..2066352167 100644
--- a/include/pdf/SkPDFTypes.h
+++ b/include/pdf/SkPDFTypes.h
@@ -108,6 +108,27 @@ private:
int32_t fValue;
};
+/** \class SkPDFBool
+
+ An boolean value in a PDF.
+*/
+class SkPDFBool : public SkPDFObject {
+public:
+ /** Create a PDF boolean.
+ * @param value true or false.
+ */
+ explicit SkPDFBool(bool value);
+ virtual ~SkPDFBool();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+ bool fValue;
+};
+
/** \class SkPDFScalar
A real number object in a PDF.
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
new file mode 100644
index 0000000000..79f0967bfc
--- /dev/null
+++ b/src/pdf/SkPDFDevice.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFDevice.h"
+
+#include "SkColor.h"
+#include "SkPaint.h"
+#include "SkPDFImage.h"
+#include "SkPDFGraphicState.h"
+#include "SkPDFTypes.h"
+#include "SkPDFStream.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+// Utility functions
+
+namespace {
+
+SkString toPDFColor(SkColor color) {
+ SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
+ SkScalar colorMax = SkIntToScalar(0xFF);
+ SkString result;
+ result.appendScalar(SkIntToScalar(SkColorGetR(color))/colorMax);
+ result.append(" ");
+ result.appendScalar(SkIntToScalar(SkColorGetG(color))/colorMax);
+ result.append(" ");
+ result.appendScalar(SkIntToScalar(SkColorGetB(color))/colorMax);
+ result.append(" ");
+ return result;
+}
+
+SkString StyleAndFillToPaintOperator(SkPaint::Style style,
+ SkPath::FillType fillType) {
+ SkString result;
+ if (style == SkPaint::kFill_Style)
+ result.append("f");
+ else if (style == SkPaint::kStrokeAndFill_Style)
+ result.append("B");
+ else if (style == SkPaint::kStroke_Style)
+ return SkString("S\n");
+
+ // Not supported yet.
+ SkASSERT(fillType != SkPath::kInverseEvenOdd_FillType);
+ SkASSERT(fillType != SkPath::kInverseWinding_FillType);
+ if (fillType == SkPath::kEvenOdd_FillType)
+ result.append("*");
+ result.append("\n");
+ return result;
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkPDFDeviceFactory::newDevice(SkBitmap::Config config,
+ int width, int height,
+ bool isOpaque, bool isForLayer) {
+ return SkNEW_ARGS(SkPDFDevice, (width, height));
+}
+
+SkPDFDevice::SkPDFDevice(int width, int height)
+ : fWidth(width),
+ fHeight(height),
+ fCurrentColor(0),
+ fCurrentTextScaleX(SK_Scalar1) {
+ // Scale and translate to move the origin from the lower left to the upper
+ // left.
+ fCurTransform.setTranslate(0, height);
+ fCurTransform.preScale(1, -1);
+ fActiveTransform.reset();
+ applyTransform(fCurTransform);
+
+ fContent.append("q\n");
+ fCurTransform.reset();
+ fActiveTransform.reset();
+}
+
+SkPDFDevice::~SkPDFDevice() {
+ fGraphicStateResources.unrefAll();
+ fXObjectResources.unrefAll();
+}
+
+void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
+ const SkRegion& region) {
+ // TODO(vandebo) handle clipping
+ setTransform(matrix);
+ fCurTransform = matrix;
+}
+
+void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
+ setNoTransform();
+
+ SkPaint newPaint = paint;
+ newPaint.setStyle(SkPaint::kFill_Style);
+ updateGSFromPaint(newPaint, NULL);
+
+ SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
+ drawRect(d, all, newPaint);
+ setTransform(fCurTransform);
+}
+
+void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
+ size_t count, const SkPoint* points,
+ const SkPaint& paint) {
+ if (count == 0)
+ return;
+
+ switch (mode) {
+ case SkCanvas::kPolygon_PointMode:
+ updateGSFromPaint(paint, NULL);
+ moveTo(points[0].fX, points[0].fY);
+ for (size_t i = 1; i < count; i++)
+ appendLine(points[i].fX, points[i].fY);
+ strokePath();
+ break;
+ case SkCanvas::kLines_PointMode:
+ updateGSFromPaint(paint, NULL);
+ for (size_t i = 0; i < count/2; i++) {
+ moveTo(points[i * 2].fX, points[i * 2].fY);
+ appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
+ strokePath();
+ }
+ break;
+ case SkCanvas::kPoints_PointMode:
+ if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
+ updateGSFromPaint(paint, NULL);
+ for (size_t i = 0; i < count; i++) {
+ moveTo(points[i].fX, points[i].fY);
+ strokePath();
+ }
+ } else {
+ // PDF won't draw a single point with square/butt caps because
+ // the orientation is ambiguous. Draw a rectangle instead.
+ SkPaint newPaint = paint;
+ newPaint.setStyle(SkPaint::kFill_Style);
+ SkScalar strokeWidth = paint.getStrokeWidth();
+ SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
+ for (size_t i = 0; i < count; i++) {
+ SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
+ 0, 0);
+ r.inset(-halfStroke, -halfStroke);
+ drawRect(d, r, newPaint);
+ }
+ }
+ break;
+ default:
+ SkASSERT(false);
+ }
+}
+
+void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
+ const SkPaint& paint) {
+ if (paint.getPathEffect()) {
+ // Draw a path instead.
+ SkPath path;
+ path.addRect(r);
+ paint.getFillPath(path, &path);
+
+ SkPaint no_effect_paint(paint);
+ SkSafeUnref(no_effect_paint.setPathEffect(NULL));
+ drawPath(d, path, no_effect_paint);
+ return;
+ }
+ updateGSFromPaint(paint, NULL);
+
+ // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
+ SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
+ appendRectangle(r.fLeft, bottom, r.width(), r.height());
+ fContent.append(StyleAndFillToPaintOperator(paint.getStyle(),
+ SkPath::kWinding_FillType));
+}
+
+void SkPDFDevice::drawPath(const SkDraw&, const SkPath& path,
+ const SkPaint& paint) {
+ SkASSERT(false);
+}
+
+void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ SkMatrix scaled;
+ // Adjust for origin flip.
+ scaled.setScale(1, -1);
+ scaled.postTranslate(0, 1);
+ scaled.postConcat(fCurTransform);
+ // Scale the image up from 1x1 to WxH.
+ scaled.postScale(bitmap.width(), bitmap.height());
+ scaled.postConcat(matrix);
+ internalDrawBitmap(scaled, bitmap, paint);
+}
+
+void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) {
+ SkMatrix scaled;
+ // Adjust for origin flip.
+ scaled.setScale(1, -1);
+ scaled.postTranslate(0, 1);
+ // Scale the image up from 1x1 to WxH.
+ scaled.postScale(bitmap.width(), -bitmap.height());
+ scaled.postTranslate(x, y);
+ internalDrawBitmap(scaled, bitmap, paint);
+}
+
+void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ SkASSERT(false);
+}
+
+void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint) {
+ SkASSERT(false);
+}
+
+void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ SkASSERT(false);
+}
+
+void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
+ int vertexCount, const SkPoint verts[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ SkASSERT(false);
+}
+
+void SkPDFDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&) {
+ SkASSERT(false);
+}
+
+const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
+ if (fResourceDict.get() == NULL) {
+ fResourceDict = new SkPDFDict;
+ fResourceDict->unref(); // SkRefPtr and new both took a reference.
+
+ if (fGraphicStateResources.count()) {
+ SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
+ extGState->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fGraphicStateResources.count(); i++) {
+ SkString nameString("G");
+ nameString.appendS32(i);
+ SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
+ name->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFObjRef> gsRef =
+ new SkPDFObjRef(fGraphicStateResources[i]);
+ gsRef->unref(); // SkRefPtr and new both took a reference.
+ extGState->insert(name.get(), gsRef.get());
+ }
+ fResourceDict->insert("ExtGState", extGState.get());
+ }
+
+ if (fXObjectResources.count()) {
+ SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
+ xObjects->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fXObjectResources.count(); i++) {
+ SkString nameString("X");
+ nameString.appendS32(i);
+ SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
+ name->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFObjRef> xObjRef =
+ new SkPDFObjRef(fXObjectResources[i]);
+ xObjRef->unref(); // SkRefPtr and new both took a reference.
+ xObjects->insert(name.get(), xObjRef.get());
+ }
+ fResourceDict->insert("XObject", xObjects.get());
+ }
+ }
+ return fResourceDict;
+}
+
+void SkPDFDevice::getResouces(SkTDArray<SkPDFObject*>* resouceList) {
+ resouceList->setReserve(resouceList->count() +
+ fGraphicStateResources.count() +
+ fXObjectResources.count());
+ for (int i = 0; i < fGraphicStateResources.count(); i++) {
+ resouceList->push(fGraphicStateResources[i]);
+ fGraphicStateResources[i]->ref();
+ }
+ for (int i = 0; i < fXObjectResources.count(); i++) {
+ resouceList->push(fXObjectResources[i]);
+ fXObjectResources[i]->ref();
+ }
+}
+
+SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() {
+ SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
+ zero->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
+ width->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
+ height->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
+ mediaBox->unref(); // SkRefPtr and new both took a reference.
+ mediaBox->reserve(4);
+ mediaBox->append(zero.get());
+ mediaBox->append(zero.get());
+ mediaBox->append(width.get());
+ mediaBox->append(height.get());
+ return mediaBox;
+}
+
+SkString SkPDFDevice::content() {
+ SkString result = fContent;
+ result.append("Q");
+ return result;
+}
+
+// Private
+
+// TODO(vandebo) handle these cases.
+#define PAINTCHECK(x,y) do { \
+ if(newPaint.x() y) { \
+ printf("!!" #x #y "\n"); \
+ SkASSERT(false); \
+ } \
+ } while(0)
+
+void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
+ SkString* textStateUpdate) {
+ PAINTCHECK(getXfermode, != NULL);
+ PAINTCHECK(getPathEffect, != NULL);
+ PAINTCHECK(getMaskFilter, != NULL);
+ PAINTCHECK(getShader, != NULL);
+ PAINTCHECK(getColorFilter, != NULL);
+ PAINTCHECK(isFakeBoldText, == true);
+ PAINTCHECK(isUnderlineText, == true);
+ PAINTCHECK(isStrikeThruText, == true);
+ PAINTCHECK(getTextSkewX, != 0);
+
+ SkRefPtr<SkPDFGraphicState> newGraphicState =
+ SkPDFGraphicState::getGraphicStateForPaint(newPaint);
+ newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
+ // newGraphicState has been canonicalized so we can directly compare
+ // pointers.
+ if (fCurrentGraphicState.get() != newGraphicState.get()) {
+ int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
+ if (resourceIndex < 0) {
+ resourceIndex = fGraphicStateResources.count();
+ fGraphicStateResources.push(newGraphicState.get());
+ newGraphicState->ref();
+ }
+ fContent.append("/G");
+ fContent.appendS32(resourceIndex);
+ fContent.append(" gs\n");
+ fCurrentGraphicState = newGraphicState;
+ }
+
+ SkColor newColor = newPaint.getColor();
+ newColor = SkColorSetA(newColor, 0xFF);
+ if (fCurrentColor != newColor) {
+ SkString colorString = toPDFColor(newColor);
+ fContent.append(colorString);
+ fContent.append("RG ");
+ fContent.append(colorString);
+ fContent.append("rg\n");
+ fCurrentColor = newColor;
+ }
+
+ if (textStateUpdate != NULL &&
+ fCurrentTextScaleX != newPaint.getTextScaleX()) {
+ SkScalar scale = newPaint.getTextScaleX();
+ SkScalar pdfScale = scale * 100;
+ textStateUpdate->appendScalar(pdfScale);
+ textStateUpdate->append(" Tz\n");
+ fCurrentTextScaleX = scale;
+ }
+}
+
+void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
+ fContent.appendScalar(x);
+ fContent.append(" ");
+ fContent.appendScalar(y);
+ fContent.append(" m\n");
+}
+
+void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
+ fContent.appendScalar(x);
+ fContent.append(" ");
+ fContent.appendScalar(y);
+ fContent.append(" l\n");
+}
+
+void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+ SkScalar ctl2X, SkScalar ctl2Y,
+ SkScalar dstX, SkScalar dstY) {
+ SkString cmd("y\n");
+ fContent.appendScalar(ctl1X);
+ fContent.append(" ");
+ fContent.appendScalar(ctl1Y);
+ fContent.append(" ");
+ if (ctl2X != dstX || ctl2Y != dstY) {
+ cmd.set("c\n");
+ fContent.appendScalar(ctl2X);
+ fContent.append(" ");
+ fContent.appendScalar(ctl2Y);
+ fContent.append(" ");
+ }
+ fContent.appendScalar(dstX);
+ fContent.append(" ");
+ fContent.appendScalar(dstY);
+ fContent.append(cmd);
+}
+
+void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
+ SkScalar w, SkScalar h) {
+ fContent.appendScalar(x);
+ fContent.append(" ");
+ fContent.appendScalar(y);
+ fContent.append(" ");
+ fContent.appendScalar(w);
+ fContent.append(" ");
+ fContent.appendScalar(h);
+ fContent.append(" re\n");
+}
+
+void SkPDFDevice::closePath() {
+ fContent.append("h\n");
+}
+
+void SkPDFDevice::strokePath() {
+ fContent.append(StyleAndFillToPaintOperator(SkPaint::kStroke_Style,
+ SkPath::kWinding_FillType));
+}
+
+void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
+ const SkBitmap& bitmap,
+ const SkPaint& paint) {
+ setTransform(matrix);
+ SkPDFImage* image = new SkPDFImage(bitmap, paint);
+ fXObjectResources.push(image); // Transfer reference.
+ fContent.append("/X");
+ fContent.appendS32(fXObjectResources.count() - 1);
+ fContent.append(" Do\n");
+ setTransform(fCurTransform);
+}
+
+void SkPDFDevice::setTransform(const SkMatrix& m) {
+ setNoTransform();
+ applyTransform(m);
+}
+
+void SkPDFDevice::setNoTransform() {
+ if (fActiveTransform.getType() == SkMatrix::kIdentity_Mask)
+ return;
+ fContent.append("Q q "); // Restore the default transform and save it.
+ fCurrentGraphicState = NULL;
+ fActiveTransform.reset();
+}
+
+void SkPDFDevice::applyTempTransform(const SkMatrix& m) {
+ fContent.append("q ");
+ applyTransform(m);
+}
+
+void SkPDFDevice::removeTempTransform() {
+ fContent.append("Q\n");
+ fActiveTransform = fCurTransform;
+}
+
+void SkPDFDevice::applyTransform(const SkMatrix& m) {
+ if (m == fActiveTransform)
+ return;
+ SkASSERT((m.getType() & SkMatrix::kPerspective_Mask) == 0);
+
+ fContent.appendScalar(m[SkMatrix::kMScaleX]);
+ fContent.append(" ");
+ fContent.appendScalar(m[SkMatrix::kMSkewY]);
+ fContent.append(" ");
+ fContent.appendScalar(m[SkMatrix::kMSkewX]);
+ fContent.append(" ");
+ fContent.appendScalar(m[SkMatrix::kMScaleY]);
+ fContent.append(" ");
+ fContent.appendScalar(m[SkMatrix::kMTransX]);
+ fContent.append(" ");
+ fContent.appendScalar(m[SkMatrix::kMTransY]);
+ fContent.append(" cm\n");
+ fActiveTransform = m;
+}
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
new file mode 100644
index 0000000000..d0d47b5e6f
--- /dev/null
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFGraphicState.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+
+SkPDFGraphicState::~SkPDFGraphicState() {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ int index = find(fPaint);
+ SkASSERT(index >= 0);
+ canonicalPaints().removeShuffle(index);
+}
+
+void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ populateDict();
+ SkPDFDict::emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ populateDict();
+ return SkPDFDict::getOutputSize(catalog, indirect);
+}
+
+// static
+SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
+SkPDFGraphicState::canonicalPaints() {
+ // This initialization is only thread safe with gcc.
+ static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
+ return gCanonicalPaints;
+}
+
+// static
+SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
+ // This initialization is only thread safe with gcc.
+ static SkMutex gCanonicalPaintsMutex;
+ return gCanonicalPaintsMutex;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
+ const SkPaint& paint) {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ int index = find(paint);
+ if (index >= 0) {
+ canonicalPaints()[index].fGraphicState->ref();
+ return canonicalPaints()[index].fGraphicState;
+ }
+ GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
+ canonicalPaints().push(newEntry);
+ return newEntry.fGraphicState;
+}
+
+// static
+int SkPDFGraphicState::find(const SkPaint& paint) {
+ GSCanonicalEntry search(&paint);
+ return canonicalPaints().find(search);
+}
+
+SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
+ : fPaint(paint),
+ fPopulated(false) {
+}
+
+// populateDict and operator== have to stay in sync with each other.
+void SkPDFGraphicState::populateDict() {
+ if (!fPopulated) {
+ fPopulated = true;
+ SkRefPtr<SkPDFName> typeName = new SkPDFName("ExtGState");
+ typeName->unref(); // SkRefPtr and new both took a reference.
+ insert("Type", typeName.get());
+
+ SkScalar maxAlpha = SkIntToScalar(0xFF);
+ SkRefPtr<SkPDFScalar> alpha =
+ new SkPDFScalar(SkColorGetA(fPaint.getColor())/maxAlpha);
+ alpha->unref(); // SkRefPtr and new both took a reference.
+ insert("CA", alpha.get());
+ insert("ca", alpha.get());
+
+ SkASSERT(SkPaint::kButt_Cap == 0);
+ SkASSERT(SkPaint::kRound_Cap == 1);
+ SkASSERT(SkPaint::kSquare_Cap == 2);
+ SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
+ SkRefPtr<SkPDFInt> strokeCap = new SkPDFInt(fPaint.getStrokeCap());
+ strokeCap->unref(); // SkRefPtr and new both took a reference.
+ insert("LC", strokeCap.get());
+
+ SkASSERT(SkPaint::kMiter_Join == 0);
+ SkASSERT(SkPaint::kRound_Join == 1);
+ SkASSERT(SkPaint::kBevel_Join == 2);
+ SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
+ SkRefPtr<SkPDFInt> strokeJoin = new SkPDFInt(fPaint.getStrokeJoin());
+ strokeJoin->unref(); // SkRefPtr and new both took a reference.
+ insert("LJ", strokeJoin.get());
+
+ /* TODO(vandebo) Font.
+ if (fPaint.getTypeFace() != NULL) {
+ SkRefPtr<SkPDFTypeFace> typeFace =
+ SkPDFTypeFace::getFontForTypeFace(fPaint.getTypeFace);
+ SkRefPtr<SkPDFObjRef> typeFaceRef = new SkPDFObjRef(typeFace.get());
+ fontRef->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFScalar> fontSize =
+ new SkPDFScalar(fPaint.getTetSize());
+ fontSize->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFArray> font = new SkPDFArray();
+ font->unref(); // SkRefPtr and new both took a reference.
+ font->reserve(2);
+ font->append(typeFaceRef.get());
+ font->append(fontSize.get());
+ insert("LJ", font.get());
+ }
+ */
+
+ SkRefPtr<SkPDFScalar> strokeWidth =
+ new SkPDFScalar(fPaint.getStrokeWidth());
+ strokeWidth->unref(); // SkRefPtr and new both took a reference.
+ insert("LW", strokeWidth.get());
+
+ SkRefPtr<SkPDFScalar> strokeMiterLimit = new SkPDFScalar(
+ fPaint.getStrokeMiter());
+ strokeMiterLimit->unref(); // SkRefPtr and new both took a reference.
+ insert("ML", strokeWidth.get());
+
+ // Turn on automatic stroke adjustment.
+ SkRefPtr<SkPDFBool> trueVal = new SkPDFBool(true);
+ trueVal->unref(); // SkRefPtr and new both took a reference.
+ insert("SA", trueVal.get());
+ }
+}
+
+// We're only interested in some fields of the SkPaint, so we have a custom
+// operator== function.
+bool SkPDFGraphicState::GSCanonicalEntry::operator==(
+ const SkPDFGraphicState::GSCanonicalEntry& gs) const {
+ const SkPaint* a = fPaint;
+ const SkPaint* b = gs.fPaint;
+ SkASSERT(a != NULL);
+ SkASSERT(b != NULL);
+ SkTypeface* aFace = a->getTypeface();
+ SkTypeface* bFace = b->getTypeface();
+ return SkColorGetA(a->getColor()) == SkColorGetA(b->getColor()) &&
+ a->getStrokeCap() == b->getStrokeCap() &&
+ a->getStrokeJoin() == b->getStrokeJoin() &&
+ a->getTextSize() == b->getTextSize() &&
+ a->getStrokeWidth() == b->getStrokeWidth() &&
+ a->getStrokeMiter() == b->getStrokeMiter() &&
+ (aFace == NULL) == (bFace == NULL) &&
+ (aFace == NULL || aFace->uniqueID() == bFace->uniqueID());
+}
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
new file mode 100644
index 0000000000..b480089b17
--- /dev/null
+++ b/src/pdf/SkPDFImage.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFImage.h"
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkPaint.h"
+#include "SkPackBits.h"
+#include "SkPDFCatalog.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkUnPreMultiply.h"
+
+namespace {
+
+SkMemoryStream* extractImageData(const SkBitmap& bitmap) {
+ SkMemoryStream* result;
+
+ switch (bitmap.getConfig()) {
+ case SkBitmap::kIndex8_Config:
+ result = new SkMemoryStream(bitmap.getPixels(), bitmap.getSize(),
+ true);
+ break;
+ case SkBitmap::kRLE_Index8_Config: {
+ result = new SkMemoryStream(bitmap.getSize());
+ const SkBitmap::RLEPixels* rle =
+ (const SkBitmap::RLEPixels*)bitmap.getPixels();
+ uint8_t* dst = (uint8_t*)result->getMemoryBase();
+ const int width = bitmap.width();
+ for (int y = 0; y < bitmap.height(); y++) {
+ SkPackBits::Unpack8(rle->packedAtY(y), width, dst);
+ dst += width;
+ }
+ break;
+ }
+ case SkBitmap::kARGB_4444_Config: {
+ const int width = bitmap.width();
+ const int rowBytes = (width * 3 + 1) / 2;
+ result = new SkMemoryStream(rowBytes * bitmap.height());
+ uint8_t* dst = (uint8_t*)result->getMemoryBase();
+ for (int y = 0; y < bitmap.height(); y++) {
+ uint16_t* src = bitmap.getAddr16(0, y);
+ for (int x = 0; x < width; x += 2) {
+ dst[0] = (SkGetPackedR4444(src[0]) << 4) |
+ SkGetPackedG4444(src[0]);
+ dst[1] = (SkGetPackedB4444(src[0]) << 4) |
+ SkGetPackedR4444(src[1]);
+ dst[2] = (SkGetPackedG4444(src[1]) << 4) |
+ SkGetPackedB4444(src[1]);
+ src += 2;
+ dst += 3;
+ }
+ if (width & 1) {
+ dst[0] = (SkGetPackedR4444(src[0]) << 4) |
+ SkGetPackedG4444(src[0]);
+ dst[1] = (SkGetPackedB4444(src[0]) << 4);
+ }
+ }
+ break;
+ }
+ case SkBitmap::kRGB_565_Config: {
+ const int width = bitmap.width();
+ const int rowBytes = width * 3;
+ result = new SkMemoryStream(rowBytes * bitmap.height());
+ uint8_t* dst = (uint8_t*)result->getMemoryBase();
+ for (int y = 0; y < bitmap.height(); y++) {
+ uint16_t* src = bitmap.getAddr16(0, y);
+ for (int x = 0; x < width; x++) {
+ dst[0] = SkGetPackedR16(src[0]);
+ dst[1] = SkGetPackedG16(src[0]);
+ dst[2] = SkGetPackedB16(src[0]);
+ src++;
+ dst += 3;
+ }
+ }
+ break;
+ }
+ case SkBitmap::kARGB_8888_Config: {
+ const int width = bitmap.width();
+ const int rowBytes = width * 3;
+ result = new SkMemoryStream(rowBytes * bitmap.height());
+ uint8_t* dst = (uint8_t*)result->getMemoryBase();
+ for (int y = 0; y < bitmap.height(); y++) {
+ uint32_t* src = bitmap.getAddr32(0, y);
+ for (int x = 0; x < width; x++) {
+ dst[0] = SkGetPackedR32(src[0]);
+ dst[1] = SkGetPackedG32(src[0]);
+ dst[2] = SkGetPackedB32(src[0]);
+ src++;
+ dst += 3;
+ }
+ }
+ break;
+ }
+ default:
+ SkASSERT(false);
+ }
+ return result;
+}
+
+SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
+ SkPDFArray* result = new SkPDFArray();
+ result->reserve(4);
+ SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
+ indexedName->unref(); // SkRefPtr and new both took a reference.
+ result->append(indexedName.get());
+
+ SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
+ rgbName->unref(); // SkRefPtr and new both took a reference.
+ result->append(rgbName.get());
+
+ rgbName->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
+ result->append(countValue.get());
+
+ // Potentially, this could be represented in fewer bytes with a stream.
+ // Max size as a string is 1.5k.
+ SkString index;
+ for (int i = 0; i < table->count(); i++) {
+ char buf[3];
+ SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
+ buf[0] = SkGetPackedR32(color);
+ buf[1] = SkGetPackedG32(color);
+ buf[2] = SkGetPackedB32(color);
+ index.append(buf, 3);
+ }
+ SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
+ indexValue->unref(); // SkRefPtr and new both took a reference.
+ result->append(indexValue.get());
+ return result;
+}
+
+}; // namespace
+
+SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) {
+ SkBitmap::Config config = bitmap.getConfig();
+
+ // TODO(vandebo) Handle alpha and alpha only images correctly.
+ SkASSERT(config == SkBitmap::kRGB_565_Config ||
+ config == SkBitmap::kARGB_4444_Config ||
+ config == SkBitmap::kARGB_8888_Config ||
+ config == SkBitmap::kIndex8_Config ||
+ config == SkBitmap::kRLE_Index8_Config);
+
+ SkMemoryStream* image_data = extractImageData(bitmap);
+ SkAutoUnref image_data_unref(image_data);
+ fStream = new SkPDFStream(image_data);
+ fStream->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
+ typeValue->unref(); // SkRefPtr and new both took a reference.
+ insert("Type", typeValue.get());
+
+ SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
+ subTypeValue->unref(); // SkRefPtr and new both took a reference.
+ insert("Subtype", subTypeValue.get());
+
+ SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width());
+ widthValue->unref(); // SkRefPtr and new both took a reference.
+ insert("Width", widthValue.get());
+
+ SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height());
+ heightValue->unref(); // SkRefPtr and new both took a reference.
+ insert("Height", heightValue.get());
+
+ // if (!image mask) {
+ SkRefPtr<SkPDFObject> colorSpaceValue;
+ if (config == SkBitmap::kIndex8_Config ||
+ config == SkBitmap::kRLE_Index8_Config) {
+ colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
+ } else {
+ colorSpaceValue = new SkPDFName("DeviceRGB");
+ }
+ colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
+ insert("ColorSpace", colorSpaceValue.get());
+ // }
+
+ int bitsPerComp = bitmap.bytesPerPixel() * 2;
+ if (bitsPerComp == 0) {
+ SkASSERT(config == SkBitmap::kA1_Config);
+ bitsPerComp = 1;
+ } else if (bitsPerComp == 2 ||
+ (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
+ bitsPerComp = 8;
+ }
+ SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
+ bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
+ insert("BitsPerComponent", bitsPerCompValue.get());
+
+ if (config == SkBitmap::kRGB_565_Config) {
+ SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
+ zeroVal->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
+ scale5Val->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
+ scale6Val->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
+ decodeValue->unref(); // SkRefPtr and new both took a reference.
+ decodeValue->reserve(6);
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale5Val.get());
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale6Val.get());
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale5Val.get());
+ insert("Decode", decodeValue.get());
+ }
+}
+
+SkPDFImage::~SkPDFImage() {}
+
+void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ fStream->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ return fStream->getOutputSize(catalog, indirect);
+}
+
+void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
+ fStream->insert(key, value);
+}
+
+void SkPDFImage::insert(const char key[], SkPDFObject* value) {
+ fStream->insert(key, value);
+}
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index b880d4a26a..8be4f118ec 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -35,7 +35,7 @@ void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
fDict.emitObject(stream, catalog, false);
stream->writeText(" stream\n");
stream->write(fData->getMemoryBase(), fData->read(NULL, 0));
- stream->writeText("endstream");
+ stream->writeText("\nendstream");
}
size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
@@ -43,7 +43,7 @@ size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
return getIndirectOutputSize(catalog);
return fDict.getOutputSize(catalog, false) +
- strlen(" stream\nendstream") + fData->read(NULL, 0);
+ strlen(" stream\n\nendstream") + fData->read(NULL, 0);
}
void SkPDFStream::insert(SkPDFName* key, SkPDFObject* value) {
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index a01fcbaf5d..27668ab287 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -64,6 +64,26 @@ void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
stream->writeDecAsText(fValue);
}
+SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
+SkPDFBool::~SkPDFBool() {}
+
+void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ SkASSERT(!indirect);
+ if (fValue) {
+ stream->writeText("true");
+ } else {
+ stream->writeText("false");
+ }
+}
+
+size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ SkASSERT(!indirect);
+ if (fValue)
+ return strlen("true");
+ return strlen("false");
+}
+
SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
SkPDFScalar::~SkPDFScalar() {}
diff --git a/src/pdf/pdf_files.mk b/src/pdf/pdf_files.mk
index a28957f5c5..5ef776f240 100644
--- a/src/pdf/pdf_files.mk
+++ b/src/pdf/pdf_files.mk
@@ -1,6 +1,9 @@
SOURCE := \
SkPDFCatalog.cpp \
+ SkPDFDevice.cpp \
SkPDFDocument.cpp \
+ SkPDFGraphicState.cpp \
+ SkPDFImage.cpp \
SkPDFPage.cpp \
SkPDFStream.cpp \
SkPDFTypes.cpp \
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 5dcb73cd6f..6d33a010f7 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -175,12 +175,12 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
stream->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, stream.get(),
- "<</Length 12\n>> stream\nTest\nFoo\tBarendstream",
+ "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream",
true);
stream->insert(n1.get(), int42.get());
CheckObjectOutput(reporter, stream.get(),
"<</Length 12\n/n1 42\n>> stream\nTest\nFoo\tBar"
- "endstream",
+ "\nendstream",
true);
TestCatalog(reporter);