Reviewed by Hyatt.
[WebKit-https.git] / WebCore / platform / mac / PDFDocumentImage.mm
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 #import "config.h"
27 #import "Image.h"
28 #import "PDFDocumentImage.h"
29
30 namespace WebCore {
31
32 static void releasePDFDocumentData(void *info, const void *data, size_t size)
33 {
34     [(id)info autorelease];
35 }
36
37 PDFDocumentImage::PDFDocumentImage(NSData* data)
38 {
39     if (data != nil) {
40         CGDataProviderRef dataProvider = CGDataProviderCreateWithData([data retain], [data bytes], [data length], releasePDFDocumentData);
41         m_document = CGPDFDocumentCreateWithProvider(dataProvider);
42         CGDataProviderRelease(dataProvider);
43     }
44     m_currentPage = -1;
45     setCurrentPage(0);
46 }
47
48 PDFDocumentImage::~PDFDocumentImage()
49 {
50     CGPDFDocumentRelease(m_document);
51 }
52
53 CGPDFDocumentRef PDFDocumentImage::documentRef()
54 {
55     return m_document;
56 }
57
58 CGRect PDFDocumentImage::mediaBox()
59 {
60     return m_mediaBox;
61 }
62
63 CGRect PDFDocumentImage::bounds()
64 {
65     CGRect rotatedRect;
66
67     // rotate the media box and calculate bounding box
68     float sina   = sinf(m_rotation);
69     float cosa   = cosf(m_rotation);
70     float width  = m_cropBox.size.width;
71     float height = m_cropBox.size.height;
72
73     // calculate rotated x and y axis
74     NSPoint rx = NSMakePoint( width  * cosa, width  * sina);
75     NSPoint ry = NSMakePoint(-height * sina, height * cosa);
76
77     // find delta width and height of rotated points
78     rotatedRect.origin      = m_cropBox.origin;
79     rotatedRect.size.width  = ceilf(fabs(rx.x - ry.x));
80     rotatedRect.size.height = ceilf(fabs(ry.y - rx.y));
81
82     return rotatedRect;
83 }
84
85 void PDFDocumentImage::adjustCTM(CGContextRef context)
86 {
87     // rotate the crop box and calculate bounding box
88     float sina   = sinf(-m_rotation);
89     float cosa   = cosf(-m_rotation);
90     float width  = m_cropBox.size.width;
91     float height = m_cropBox.size.height;
92
93     // calculate rotated x and y edges of the corp box. if they're negative, it means part of the image has
94     // been rotated outside of the bounds and we need to shift over the image so it lies inside the bounds again
95     NSPoint rx = NSMakePoint( width  * cosa, width  * sina);
96     NSPoint ry = NSMakePoint(-height * sina, height * cosa);
97
98     // adjust so we are at the crop box origin
99     CGContextTranslateCTM(context, floorf(-MIN(0,MIN(rx.x, ry.x))), floorf(-MIN(0,MIN(rx.y, ry.y))));
100
101     // rotate -ve to remove rotation
102     CGContextRotateCTM(context, -m_rotation);
103
104     // shift so we are completely within media box
105     CGContextTranslateCTM(context, m_mediaBox.origin.x - m_cropBox.origin.x, m_mediaBox.origin.y - m_cropBox.origin.y);
106 }
107
108 void PDFDocumentImage::setCurrentPage(int page)
109 {
110     if (page != m_currentPage && page >= 0 && page < pageCount()) {
111
112         CGRect r;
113
114         m_currentPage = page;
115
116         // get media box (guaranteed)
117         m_mediaBox = CGPDFDocumentGetMediaBox(m_document, page + 1);
118
119         // get crop box (not always there). if not, use _mediaBox
120         r = CGPDFDocumentGetCropBox(m_document, page + 1);
121         if (!CGRectIsEmpty(r)) {
122             m_cropBox = CGRectMake(r.origin.x, r.origin.y, r.size.width, r.size.height);
123         } else {
124             m_cropBox = CGRectMake(m_mediaBox.origin.x, m_mediaBox.origin.y, m_mediaBox.size.width, m_mediaBox.size.height);
125         }
126
127         // get page rotation angle
128         m_rotation = CGPDFDocumentGetRotationAngle(m_document, page + 1) * M_PI / 180.0; // to radians
129     }
130 }
131
132 int PDFDocumentImage::currentPage()
133 {
134     return m_currentPage;
135 }
136
137 int PDFDocumentImage::pageCount()
138 {
139     return CGPDFDocumentGetNumberOfPages(m_document);
140 }
141
142 void PDFDocumentImage::draw(NSRect srcRect, NSRect dstRect, CompositeOperator op, 
143                             float alpha, bool flipped, CGContextRef context)
144 {
145     CGContextSaveGState(context);
146
147     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
148     [[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO] setCompositingOperation:(NSCompositingOperation)op];
149     [pool release];
150
151     // Scale and translate so the document is rendered in the correct location.
152     float hScale = dstRect.size.width  / srcRect.size.width;
153     float vScale = dstRect.size.height / srcRect.size.height;
154
155     CGContextTranslateCTM(context, dstRect.origin.x - srcRect.origin.x * hScale, dstRect.origin.y - srcRect.origin.y * vScale);
156     CGContextScaleCTM(context, hScale, vScale);
157
158     // Reverse if flipped image.
159     if (flipped) {
160         CGContextScaleCTM(context, 1, -1);
161         CGContextTranslateCTM(context, 0, -dstRect.size.height);
162     }
163
164     // Clip to destination in case we are imaging part of the source only
165     CGContextClipToRect(context, CGRectIntegral(*(CGRect*)&srcRect));
166
167     // and draw
168     if (m_document) {
169         CGContextSaveGState(context);
170         // Rotate translate image into position according to doc properties.
171         adjustCTM(context);
172
173         // Media box may have non-zero origin which we ignore. CGPDFDocumentShowPage pages start
174         // at 1, not 0.
175         CGContextDrawPDFDocument(context, CGRectMake(0, 0, m_mediaBox.size.width, m_mediaBox.size.height), m_document, 1);
176
177         CGContextRestoreGState(context);
178     }
179
180     // done with our fancy transforms
181     CGContextRestoreGState(context);
182 }
183
184 }