Commit 0f81d35a authored by mrowe@apple.com's avatar mrowe@apple.com

REGRESSION (WebKit2): Save as PDF no longer generates links to URLs

<http://webkit.org/b/65076> / <rdar://problem/9606246>

WebKit2 printing works by having the web process render the page content to a PDF. The PDF
data is then shipped to the UI process which will render it in to the printing graphics context.
Links were being lost because the API used to do the rendering of the PDF in to the printing
graphics context, CGContextDrawPDFPage, did not preserve the links that were present in the
PDF content received from the web process.

To fix this we switch to using PDFKit for drawing the PDF in to the printing graphics context.
PDFKit provides the ability for us to iterate over the links in the PDF content ourselves and
add links in to the printing graphics context.

Reviewed by Alexey Proskuryakov.

* UIProcess/API/mac/WKPrintingView.h:
* UIProcess/API/mac/WKPrintingView.mm:
(pdfAnnotationLinkClass): Look up the PDFAnnotationLink class from PDFKit as WebKit2 loads PDFKit lazily.
(pdfDocumentClass): Ditto.
(-[WKPrintingView _drawPDFDocument:page:atPoint:]): Switch to using the PDFKit equivalents of several types.
Iterate over the annotations present in the PDFPage, calling CGPDFContextSetURLForRect for each PDFAnnotationLink
that we find.
(-[WKPrintingView _drawPreview:]): Create an NSData to feed to PDFDocument.
(-[WKPrintingView drawRect:]): Ditto.
* WebKit2Prefix.h: Add the usual workaround to make Objective-C exceptions compile when C++ exception handling is disabled.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@104377 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent fac10325
2012-01-06 Mark Rowe <mrowe@apple.com>
REGRESSION (WebKit2): Save as PDF no longer generates links to URLs
<http://webkit.org/b/65076> / <rdar://problem/9606246>
WebKit2 printing works by having the web process render the page content to a PDF. The PDF
data is then shipped to the UI process which will render it in to the printing graphics context.
Links were being lost because the API used to do the rendering of the PDF in to the printing
graphics context, CGContextDrawPDFPage, did not preserve the links that were present in the
PDF content received from the web process.
To fix this we switch to using PDFKit for drawing the PDF in to the printing graphics context.
PDFKit provides the ability for us to iterate over the links in the PDF content ourselves and
add links in to the printing graphics context.
Reviewed by Alexey Proskuryakov.
* UIProcess/API/mac/WKPrintingView.h:
* UIProcess/API/mac/WKPrintingView.mm:
(pdfAnnotationLinkClass): Look up the PDFAnnotationLink class from PDFKit as WebKit2 loads PDFKit lazily.
(pdfDocumentClass): Ditto.
(-[WKPrintingView _drawPDFDocument:page:atPoint:]): Switch to using the PDFKit equivalents of several types.
Iterate over the annotations present in the PDFPage, calling CGPDFContextSetURLForRect for each PDFAnnotationLink
that we find.
(-[WKPrintingView _drawPreview:]): Create an NSData to feed to PDFDocument.
(-[WKPrintingView drawRect:]): Ditto.
* WebKit2Prefix.h: Add the usual workaround to make Objective-C exceptions compile when C++ exception handling is disabled.
2012-01-06 Viatcheslav Ostapenko <ostapenko.viatcheslav@nokia.com>
[Qt] [WK2] Minibrowser leaks memory ~6-7Mb per reload
......@@ -27,6 +27,7 @@
#import <wtf/RetainPtr.h>
@class WKPrintingViewData;
@class PDFDocument;
namespace WebKit {
class WebFrameProxy;
......@@ -43,7 +44,7 @@ namespace WebKit {
HashMap<WebCore::IntRect, Vector<uint8_t> > _pagePreviews;
Vector<uint8_t> _printedPagesData;
RetainPtr<CGPDFDocumentRef> _printedPagesPDFDocument;
RetainPtr<PDFDocument> _printedPagesPDFDocument;
uint64_t _expectedComputedPagesCallback;
HashMap<uint64_t, WebCore::IntRect> _expectedPreviewCallbacks;
......
......@@ -30,6 +30,7 @@
#import "PrintInfo.h"
#import "WebData.h"
#import "WebPageProxy.h"
#import <PDFKit/PDFKit.h>
#import <WebCore/WebCoreObjCExtras.h>
#import <wtf/MainThread.h>
......@@ -410,16 +411,30 @@ static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
return 0; // Invalid page number.
}
- (void)_drawPDFDocument:(CGPDFDocumentRef)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
static Class pdfAnnotationLinkClass()
{
static Class pdfAnnotationLinkClass = NSClassFromString(@"PDFAnnotationLink");
return pdfAnnotationLinkClass;
}
static Class pdfDocumentClass()
{
static Class pdfDocumentClass = NSClassFromString(@"PDFDocument");
return pdfDocumentClass;
}
- (void)_drawPDFDocument:(PDFDocument *)pdfDocument page:(unsigned)page atPoint:(NSPoint)point
{
if (!pdfDocument) {
LOG_ERROR("Couldn't create a PDF document with data passed for preview");
return;
}
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdfDocument, page);
if (!pdfPage) {
LOG_ERROR("Preview data doesn't have page %d", page);
PDFPage *pdfPage;
@try {
pdfPage = [pdfDocument pageAtIndex:page];
} @catch (id exception) {
LOG_ERROR("Preview data doesn't have page %d: %@", page, exception);
return;
}
......@@ -429,8 +444,25 @@ static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
CGContextSaveGState(context);
CGContextTranslateCTM(context, point.x, point.y);
CGContextScaleCTM(context, _totalScaleFactorForPrinting, -_totalScaleFactorForPrinting);
CGContextTranslateCTM(context, 0, -CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox).size.height);
CGContextDrawPDFPage(context, pdfPage);
CGContextTranslateCTM(context, 0, -[pdfPage boundsForBox:kPDFDisplayBoxMediaBox].size.height);
[pdfPage drawWithBox:kPDFDisplayBoxMediaBox];
CGAffineTransform transform = CGContextGetCTM(context);
for (PDFAnnotation *annotation in [pdfPage annotations]) {
if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
continue;
PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
NSURL *url = [linkAnnotation URL];
if (!url)
continue;
CGRect urlRect = NSRectToCGRect([linkAnnotation bounds]);
CGRect transformedRect = CGRectApplyAffineTransform(urlRect, transform);
CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
}
CGContextRestoreGState(context);
}
......@@ -473,11 +505,11 @@ static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
return;
}
const Vector<uint8_t>& pdfData = pagePreviewIterator->second;
RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, pdfData.data(), pdfData.size(), 0));
RetainPtr<CGPDFDocumentRef> pdfDocument(AdoptCF, CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
const Vector<uint8_t>& pdfDataBytes = pagePreviewIterator->second;
RetainPtr<NSData> pdfData(AdoptNS, [[NSData alloc] initWithBytes:pdfDataBytes.data() length:pdfDataBytes.size()]);
RetainPtr<PDFDocument> pdfDocument(AdoptNS, [[pdfDocumentClass() alloc] initWithData:pdfData.get()]);
[self _drawPDFDocument:pdfDocument.get() page:1 atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
[self _drawPDFDocument:pdfDocument.get() page:0 atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
}
- (void)drawRect:(NSRect)nsRect
......@@ -498,11 +530,11 @@ static void prepareDataForPrintingOnSecondaryThread(void* untypedContext)
ASSERT(!_printedPagesData.isEmpty()); // Prepared by knowsPageRange:
if (!_printedPagesPDFDocument) {
RetainPtr<CGDataProviderRef> pdfDataProvider(AdoptCF, CGDataProviderCreateWithData(0, _printedPagesData.data(), _printedPagesData.size(), 0));
_printedPagesPDFDocument.adoptCF(CGPDFDocumentCreateWithProvider(pdfDataProvider.get()));
RetainPtr<NSData> pdfData(AdoptNS, [[NSData alloc] initWithBytes:_printedPagesData.data() length:_printedPagesData.size()]);
_printedPagesPDFDocument.adoptNS([[pdfDocumentClass() alloc] initWithData:pdfData.get()]);
}
unsigned printedPageNumber = [self _pageForRect:nsRect] - [self _firstPrintedPageNumber] + 1;
unsigned printedPageNumber = [self _pageForRect:nsRect] - [self _firstPrintedPageNumber];
[self _drawPDFDocument:_printedPagesPDFDocument.get() page:printedPageNumber atPoint:NSMakePoint(nsRect.origin.x, nsRect.origin.y)];
}
......
......@@ -28,6 +28,20 @@
#import <Cocoa/Cocoa.h>
#endif
/* When C++ exceptions are disabled, the C++ library defines |try| and |catch|
* to allow C++ code that expects exceptions to build. These definitions
* interfere with Objective-C++ uses of Objective-C exception handlers, which
* use |@try| and |@catch|. As a workaround, undefine these macros. */
#ifdef __cplusplus
#include <algorithm> // needed for exception_defines.h
#endif
#ifdef __OBJC__
#undef try
#undef catch
#endif
#ifdef __cplusplus
#define new ("if you use new/delete make sure to include config.h at the top of the file"())
#define delete ("if you use new/delete make sure to include config.h at the top of the file"())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment