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 42using namespace std; 43 44namespace WebCore { 45 46PDFDocumentImage::PDFDocumentImage() 47 : Image(0) // PDFs don't animate 48 , m_document(0) 49 , m_rotation(0.0f) 50 , m_currentPage(-1) 51{ 52} 53 54PDFDocumentImage::~PDFDocumentImage() 55{ 56 CGPDFDocumentRelease(m_document); 57} 58 59String PDFDocumentImage::filenameExtension() const 60{ 61 return "pdf"; 62} 63 64IntSize 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 76bool 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 98void 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 122void 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 151int PDFDocumentImage::pageCount() const 152{ 153 return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0; 154} 155 156void 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