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 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 54 PDFDocumentImage::~PDFDocumentImage() 55 { 56 CGPDFDocumentRelease(m_document); 57 } 58 59 String PDFDocumentImage::filenameExtension() const 60 { 61 return "pdf"; 62 } 63 64 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 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 98 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 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 151 int PDFDocumentImage::pageCount() const 152 { 153 return m_document ? CGPDFDocumentGetNumberOfPages(m_document) : 0; 154 } 155 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