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