673de1d66d1eeff2c241ad9df239aad6cfa397b9
[WebKit-https.git] / WebCore / platform / graphics / cg / PDFDocumentImage.cpp
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 PLATFORM(CG)
31
32 #include "GraphicsContext.h"
33 #include "ImageObserver.h"
34 #if !PLATFORM(MAC)
35 #include "ImageSourceCG.h"
36 #endif
37 #include <wtf/MathExtras.h>
38
39 using namespace std;
40
41 namespace WebCore {
42
43 PDFDocumentImage::PDFDocumentImage()
44     : Image(0) // PDFs don't animate
45     , m_document(0)
46     , m_rotation(0.0f)
47     , m_currentPage(-1)
48 {
49 }
50
51 PDFDocumentImage::~PDFDocumentImage()
52 {
53     CGPDFDocumentRelease(m_document);
54 }
55
56 IntSize PDFDocumentImage::size() const
57 {
58     const float sina = sinf(-m_rotation);
59     const float cosa = cosf(-m_rotation);
60     const float width = m_mediaBox.size().width();
61     const float height = m_mediaBox.size().height();
62     const float rotWidth = width * cosa - height * sina;
63     const float rotHeight = width * sina + height * cosa;
64     
65     return IntSize((int)(fabsf(rotWidth) + 0.5f), (int)(fabsf(rotHeight) + 0.5f));
66 }
67
68 bool PDFDocumentImage::dataChanged(bool allDataReceived)
69 {
70     if (allDataReceived && !m_document) {
71 #if PLATFORM(MAC)
72         // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.  We use SharedBuffer's ability
73         // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
74         RetainPtr<CFDataRef> data(AdoptCF, this->data()->createCFData());
75         RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
76 #else
77         // Create a CGDataProvider to wrap the SharedBuffer.
78         // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
79         // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
80         // is a requirement for using the GetBytePointer callback.
81         CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, 0 };
82         RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirect(this->data(), this->data()->size(), &providerCallbacks));
83 #endif
84         m_document = CGPDFDocumentCreateWithProvider(dataProvider.get());
85         setCurrentPage(0);
86     }
87     return m_document; // return true if size is available
88 }
89
90 void PDFDocumentImage::adjustCTM(GraphicsContext* context) const
91 {
92     // rotate the crop box and calculate bounding box
93     float sina = sinf(-m_rotation);
94     float cosa = cosf(-m_rotation);
95     float width = m_cropBox.width();
96     float height = m_cropBox.height();
97
98     // calculate rotated x and y edges of the corp box. if they're negative, it means part of the image has
99     // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
100     CGPoint rx = CGPointMake(width * cosa, width * sina);
101     CGPoint ry = CGPointMake(-height * sina, height * cosa);
102
103     // adjust so we are at the crop box origin
104     const CGFloat zero = 0;
105     CGContextTranslateCTM(context->platformContext(), floorf(-min(zero, min(rx.x, ry.x))), floorf(-min(zero, min(rx.y, ry.y))));
106
107     // rotate -ve to remove rotation
108     CGContextRotateCTM(context->platformContext(), -m_rotation);
109
110     // shift so we are completely within media box
111     CGContextTranslateCTM(context->platformContext(), m_mediaBox.x() - m_cropBox.x(), m_mediaBox.y() - m_cropBox.y());
112 }
113
114 void PDFDocumentImage::setCurrentPage(int page)
115 {
116     if (!m_document)
117         return;
118
119     if (page == m_currentPage)
120         return;
121
122     if (!(page >= 0 && page < pageCount()))
123         return;
124
125     m_currentPage = page;
126
127     CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document, page + 1);
128
129     // get media box (guaranteed)
130     m_mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
131
132     // get crop box (not always there). if not, use media box
133     CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
134     if (!CGRectIsEmpty(r))
135         m_cropBox = r;
136     else
137         m_cropBox = m_mediaBox;
138
139     // get page rotation angle
140     m_rotation = CGPDFPageGetRotationAngle(cgPage) * piFloat / 180.0f; // to radians
141 }
142
143 int PDFDocumentImage::pageCount() const
144 {
145     return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0;
146 }
147
148 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op)
149 {
150     if (!m_document || m_currentPage == -1)
151         return;
152
153     context->save();
154
155     context->setCompositeOperation(op);
156
157     float hScale = dstRect.width() / srcRect.width();
158     float vScale = dstRect.height() / srcRect.height();
159
160     // Scale and translate so the document is rendered in the correct location,
161     // including accounting for the fact that a GraphicsContext is always flipped
162     // and doing appropriate flipping.
163     CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
164     CGContextScaleCTM(context->platformContext(), hScale, vScale);
165     CGContextScaleCTM(context->platformContext(), 1, -1);
166     CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
167     CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));
168
169     // Rotate translate image into position according to doc properties.
170     adjustCTM(context);
171
172     CGContextTranslateCTM(context->platformContext(), -m_mediaBox.x(), -m_mediaBox.y());
173     CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document, m_currentPage + 1));
174
175     context->restore();
176
177     if (imageObserver())
178         imageObserver()->didDraw(this);
179 }
180
181 }
182
183 #endif // PLATFORM(CG)