2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #define _USE_MATH_DEFINES 1
28 #include "PDFDocumentImage.h"
32 #include "GraphicsContext.h"
33 #include "ImageObserver.h"
35 #include "SharedBuffer.h"
36 #include <CoreGraphics/CGContext.h>
37 #include <CoreGraphics/CGPDFDocument.h>
38 #include <wtf/MathExtras.h>
39 #include <wtf/RetainPtr.h>
42 #include "ImageSourceCG.h"
49 PDFDocumentImage::PDFDocumentImage()
50 : Image(0) // PDFs don't animate
57 PDFDocumentImage::~PDFDocumentImage()
59 CGPDFDocumentRelease(m_document);
62 String PDFDocumentImage::filenameExtension() const
67 IntSize PDFDocumentImage::size() const
69 const float sina = sinf(-m_rotation);
70 const float cosa = cosf(-m_rotation);
71 const float width = m_mediaBox.size().width();
72 const float height = m_mediaBox.size().height();
73 const float rotWidth = width * cosa - height * sina;
74 const float rotHeight = width * sina + height * cosa;
76 return IntSize((int)(fabsf(rotWidth) + 0.5f), (int)(fabsf(rotHeight) + 0.5f));
79 void PDFDocumentImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio)
81 // FIXME: If we want size negotiation with PDF documents as-image, this is the place to implement it (https://bugs.webkit.org/show_bug.cgi?id=12095).
82 Image::computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
83 intrinsicRatio = FloatSize();
86 bool PDFDocumentImage::dataChanged(bool allDataReceived)
88 if (allDataReceived && !m_document) {
90 // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge. We use SharedBuffer's ability
91 // to wrap itself inside CFData to get around this, ensuring that ImageIO is really looking at the SharedBuffer.
92 RetainPtr<CFDataRef> data(AdoptCF, this->data()->createCFData());
93 RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
95 // Create a CGDataProvider to wrap the SharedBuffer.
96 // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
97 // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
98 // is a requirement for using the GetBytePointer callback.
99 CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, 0 };
100 RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateDirect(this->data(), this->data()->size(), &providerCallbacks));
102 m_document = CGPDFDocumentCreateWithProvider(dataProvider.get());
105 return m_document; // return true if size is available
108 void PDFDocumentImage::adjustCTM(GraphicsContext* context) const
110 // rotate the crop box and calculate bounding box
111 float sina = sinf(-m_rotation);
112 float cosa = cosf(-m_rotation);
113 float width = m_cropBox.width();
114 float height = m_cropBox.height();
116 // calculate rotated x and y edges of the corp box. if they're negative, it means part of the image has
117 // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
118 CGPoint rx = CGPointMake(width * cosa, width * sina);
119 CGPoint ry = CGPointMake(-height * sina, height * cosa);
121 // adjust so we are at the crop box origin
122 const CGFloat zero = 0;
123 CGContextTranslateCTM(context->platformContext(), floorf(-min(zero, min(rx.x, ry.x))), floorf(-min(zero, min(rx.y, ry.y))));
125 // rotate -ve to remove rotation
126 CGContextRotateCTM(context->platformContext(), -m_rotation);
128 // shift so we are completely within media box
129 CGContextTranslateCTM(context->platformContext(), m_mediaBox.x() - m_cropBox.x(), m_mediaBox.y() - m_cropBox.y());
132 void PDFDocumentImage::setCurrentPage(int page)
137 if (page == m_currentPage)
140 if (!(page >= 0 && page < pageCount()))
143 m_currentPage = page;
145 CGPDFPageRef cgPage = CGPDFDocumentGetPage(m_document, page + 1);
147 // get media box (guaranteed)
148 m_mediaBox = CGPDFPageGetBoxRect(cgPage, kCGPDFMediaBox);
150 // get crop box (not always there). if not, use media box
151 CGRect r = CGPDFPageGetBoxRect(cgPage, kCGPDFCropBox);
152 if (!CGRectIsEmpty(r))
155 m_cropBox = m_mediaBox;
157 // get page rotation angle
158 m_rotation = CGPDFPageGetRotationAngle(cgPage) * piFloat / 180.0f; // to radians
161 int PDFDocumentImage::pageCount() const
163 return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0;
166 void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace, CompositeOperator op)
168 if (!m_document || m_currentPage == -1)
172 GraphicsContextStateSaver stateSaver(*context);
174 context->setCompositeOperation(op);
176 float hScale = dstRect.width() / srcRect.width();
177 float vScale = dstRect.height() / srcRect.height();
179 // Scale and translate so the document is rendered in the correct location,
180 // including accounting for the fact that a GraphicsContext is always flipped
181 // and doing appropriate flipping.
182 CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale);
183 CGContextScaleCTM(context->platformContext(), hScale, vScale);
184 CGContextScaleCTM(context->platformContext(), 1, -1);
185 CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height());
186 CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect));
188 // Rotate translate image into position according to doc properties.
191 CGContextTranslateCTM(context->platformContext(), -m_mediaBox.x(), -m_mediaBox.y());
192 CGContextDrawPDFPage(context->platformContext(), CGPDFDocumentGetPage(m_document, m_currentPage + 1));
196 imageObserver()->didDraw(this);