1 /*
2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #define _USE_MATH_DEFINES 1
27 #include "config.h"
28 #include "PDFDocumentImage.h"
29
30 #if USE(CG)
31
32 #include "GraphicsContext.h"
33 #include "ImageObserver.h"
34 #include "SharedBuffer.h"
35 #include <wtf/MathExtras.h>
36 #include <wtf/RetainPtr.h>
37
38 #if !PLATFORM(MAC)
39 #include "ImageSourceCG.h"
40 #endif
41
42 using namespace std;
43
44 namespace WebCore {
45
PDFDocumentImage()46 PDFDocumentImage::PDFDocumentImage()
47 : Image(0) // PDFs don't animate
48 , m_document(0)
49 , m_rotation(0.0f)
50 , m_currentPage(-1)
51 {
52 }
53
~PDFDocumentImage()54 PDFDocumentImage::~PDFDocumentImage()
55 {
56 CGPDFDocumentRelease(m_document);
57 }
58
filenameExtension() const59 String PDFDocumentImage::filenameExtension() const
60 {
61 return "pdf";
62 }
63
size() const64 IntSize PDFDocumentImage::size() const
65 {
66 const float sina = sinf(-m_rotation);
67 const float cosa = cosf(-m_rotation);
68 const float width = m_mediaBox.size().width();
69 const float height = m_mediaBox.size().height();
70 const float rotWidth = width * cosa - height * sina;
71 const float rotHeight = width * sina + height * cosa;
72
73 return IntSize((int)(fabsf(rotWidth) + 0.5f), (int)(fabsf(rotHeight) + 0.5f));
74 }
75
dataChanged(bool allDataReceived)76 bool PDFDocumentImage::dataChanged(bool allDataReceived)
77 {
78 if (allDataReceived && !m_document) {
79 #if PLATFORM(MAC)
80 // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge. We use SharedBuffer's ability
81 // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
82 RetainPtr<CFDataRef> data(AdoptCF, this->data()->createCFData());
83 RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
84 #else
85 // Create a CGDataProvider to wrap the SharedBuffer.
86 // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
87 // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
88 // is a requirement for using the GetBytePointer callback.
89 CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, 0 };
90 RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirect(this->data(), this->data()->size(), &providerCallbacks));
91 #endif
92 m_document = CGPDFDocumentCreateWithProvider(dataProvider.get());
93 setCurrentPage(0);
94 }
95 return m_document; // return true if size is available
96 }
97
adjustCTM(GraphicsContext * context) const98 void PDFDocumentImage::adjustCTM(GraphicsContext* context) const
99 {
100 // rotate the crop box and calculate bounding box
101 float sina = sinf(-m_rotation);
102 float cosa = cosf(-m_rotation);
103 float width = m_cropBox.width();
104 float height = m_cropBox.height();
105
106 // calculate rotated x and y edges of the corp box. if they're negative, it means part of the image has
107 // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
108 CGPoint rx = CGPointMake(width * cosa, width * sina);
109 CGPoint ry = CGPointMake(-height * sina, height * cosa);
110
111 // adjust so we are at the crop box origin
112 const CGFloat zero = 0;
113 CGContextTranslateCTM(context->platformContext(), floorf(-min(zero, min(rx.x, ry.x))), floorf(-min(zero, min(rx.y, ry.y))));
114
115 // rotate -ve to remove rotation
116 CGContextRotateCTM(context->platformContext(), -m_rotation);
117
118 // shift so we are completely within media box
119 CGContextTranslateCTM(context->platformContext(), m_mediaBox.x() - m_cropBox.x(), m_mediaBox.y() - m_cropBox.y());
120 }
121
setCurrentPage(int page)122 void PDFDocumentImage::setCurrentPage(int page)
123 {
124 if (!m_document)
125 return;
126
127 if (page == m_currentPage)
128 return;
129
130 if (!(page >= 0 && page < pageCount()))
131 return;
132
133 m_currentPage = page;
134
135 CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document, page + 1);
136
137 // get media box (guaranteed)
138 m_mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
139
140 // get crop box (not always there). if not, use media box
141 CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
142 if (!CGRectIsEmpty(r))
143 m_cropBox = r;
144 else
145 m_cropBox = m_mediaBox;
146
147 // get page rotation angle
148 m_rotation = CGPDFPageGetRotationAngle(cgPage) * piFloat / 180.0f; // to radians
149 }
150
pageCount() const151 int PDFDocumentImage::pageCount() const
152 {
153 return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0;
154 }
155
draw(GraphicsContext * context,const FloatRect & dstRect,const FloatRect & srcRect,ColorSpace,CompositeOperator op)156 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op)
157 {
158 if (!m_document || m_currentPage == -1)
159 return;
160
161 context->save();
162
163 context->setCompositeOperation(op);
164
165 float hScale = dstRect.width() / srcRect.width();
166 float vScale = dstRect.height() / srcRect.height();
167
168 // Scale and translate so the document is rendered in the correct location,
169 // including accounting for the fact that a GraphicsContext is always flipped
170 // and doing appropriate flipping.
171 CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
172 CGContextScaleCTM(context->platformContext(), hScale, vScale);
173 CGContextScaleCTM(context->platformContext(), 1, -1);
174 CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
175 CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));
176
177 // Rotate translate image into position according to doc properties.
178 adjustCTM(context);
179
180 CGContextTranslateCTM(context->platformContext(), -m_mediaBox.x(), -m_mediaBox.y());
181 CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document, m_currentPage + 1));
182
183 context->restore();
184
185 if (imageObserver())
186 imageObserver()->didDraw(this);
187 }
188
189 }
190
191 #endif // USE(CG)
192