Skip to content

Commit ea69d1c

Browse files
committed
Fix PDFs with crop information on macOS.
1 parent 9ffe114 commit ea69d1c

4 files changed

Lines changed: 87 additions & 60 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch)
2121
* Add an application language setting with a system default option in YACReader and YACReaderLibrary.
2222
* Fix fullscreen mode in Windows, interaction with the OS is now possible while the apps are in fullscreen.
2323
* Improve support for multi-screen setups.
24+
* Fix PDFs with crop information on macOS.
2425

2526
### All apps
2627
* Add support for user-installed Qt image format plugins via the shared `plugins/imageformats` folder in the YACReader settings directory.

cmake/PdfBackend.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ elseif(PDF_BACKEND STREQUAL "pdfkit")
6363
message(FATAL_ERROR "pdfkit backend is macOS only")
6464
endif()
6565
target_compile_definitions(pdf_backend_iface INTERFACE USE_PDFKIT)
66+
target_link_libraries(pdf_backend_iface INTERFACE "-framework PDFKit")
6667

6768
else()
6869
message(FATAL_ERROR "Unknown PDF_BACKEND: '${PDF_BACKEND}'. Use: pdfium, poppler, pdfkit, or no_pdf")

common/pdf_comic.mm

Lines changed: 84 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,115 +8,139 @@
88
#import <AppKit/AppKit.h>
99
#import <ApplicationServices/ApplicationServices.h>
1010
#import <Foundation/Foundation.h>
11+
#import <PDFKit/PDFKit.h>
12+
#include <cmath>
13+
14+
namespace {
15+
16+
PDFDisplayBox preferredDisplayBox(PDFPage *page)
17+
{
18+
if (page == nil) {
19+
return kPDFDisplayBoxMediaBox;
20+
}
21+
22+
CGRect cropRect = CGRectStandardize([page boundsForBox:kPDFDisplayBoxCropBox]);
23+
if (cropRect.size.width > 0 && cropRect.size.height > 0) {
24+
return kPDFDisplayBoxCropBox;
25+
}
26+
27+
return kPDFDisplayBoxMediaBox;
28+
}
29+
30+
bool isValidRenderDimension(CGFloat value)
31+
{
32+
return std::isfinite(static_cast<double>(value)) && value >= 1.0;
33+
}
34+
35+
}
1136

1237
MacOSXPDFComic::MacOSXPDFComic()
38+
: document(nullptr), lastPageData(nullptr)
1339
{
1440
}
1541

1642
MacOSXPDFComic::~MacOSXPDFComic()
1743
{
18-
CGPDFDocumentRelease((CGPDFDocumentRef)document);
44+
closeComic();
1945
}
2046

2147
bool MacOSXPDFComic::openComic(const QString &path)
2248
{
49+
closeComic();
2350

24-
CFURLRef pdfFileUrl;
25-
CFStringRef str;
26-
str = CFStringCreateWithCString(kCFAllocatorDefault, path.toUtf8().data(), kCFStringEncodingUTF8);
27-
pdfFileUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, true);
28-
29-
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfFileUrl);
51+
QByteArray utf8Path = path.toUtf8();
52+
NSString *nsPath = [[NSString alloc] initWithUTF8String:utf8Path.constData()];
53+
NSURL *pdfFileUrl = [NSURL fileURLWithPath:nsPath isDirectory:NO];
54+
PDFDocument *pdf = [[PDFDocument alloc] initWithURL:pdfFileUrl];
3055

31-
document = pdf;
56+
[nsPath release];
3257

33-
CFRelease(str);
34-
CFRelease(pdfFileUrl);
58+
if (pdf == nil) {
59+
return false;
60+
}
3561

62+
document = pdf;
3663
return true;
3764
}
3865

3966
void MacOSXPDFComic::closeComic()
4067
{
41-
// CGPDFDocumentRelease((CGPDFDocumentRef)document);
68+
if (document != nullptr) {
69+
[(PDFDocument *)document release];
70+
document = nullptr;
71+
}
4272
}
4373

4474
unsigned int MacOSXPDFComic::numPages()
4575
{
46-
return (int)CGPDFDocumentGetNumberOfPages((CGPDFDocumentRef)document);
76+
PDFDocument *pdf = (PDFDocument *)document;
77+
return pdf != nil ? static_cast<unsigned int>(pdf.pageCount) : 0;
4778
}
4879

4980
QImage MacOSXPDFComic::getPage(const int pageNum)
5081
{
51-
CGPDFPageRef page = CGPDFDocumentGetPage((CGPDFDocumentRef)document, pageNum + 1);
52-
// Changed this line for the line above which is a generic line
53-
// CGPDFPageRef page = [self getPage:page_number];
82+
PDFDocument *pdf = (PDFDocument *)document;
83+
if (pdf == nil) {
84+
return QImage();
85+
}
86+
87+
PDFPage *page = [pdf pageAtIndex:pageNum];
88+
if (page == nil) {
89+
return QImage();
90+
}
5491

55-
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
56-
int width = 2560;
92+
PDFDisplayBox displayBox = preferredDisplayBox(page);
93+
CGRect sourceRect = CGRectStandardize([page boundsForBox:displayBox]);
94+
if (!isValidRenderDimension(sourceRect.size.width) || !isValidRenderDimension(sourceRect.size.height)) {
95+
return QImage();
96+
}
5797

58-
// NSLog(@"-----%f",pageRect.size.width);
59-
CGFloat pdfScale = float(width) / pageRect.size.width;
98+
const CGFloat targetWidth = 2560.0;
99+
CGFloat pdfScale = targetWidth / sourceRect.size.width;
100+
CGSize renderSize = CGSizeMake(floor(sourceRect.size.width * pdfScale), floor(sourceRect.size.height * pdfScale));
101+
if (!isValidRenderDimension(renderSize.width) || !isValidRenderDimension(renderSize.height)) {
102+
return QImage();
103+
}
60104

61-
pageRect.size = CGSizeMake(pageRect.size.width * pdfScale, pageRect.size.height * pdfScale);
62-
pageRect.origin = CGPointZero;
105+
const int imageWidth = static_cast<int>(renderSize.width);
106+
const int imageHeight = static_cast<int>(renderSize.height);
63107

64108
CGColorSpaceRef genericColorSpace = CGColorSpaceCreateDeviceRGB();
65109

66-
QImage renderImage = QImage(pageRect.size.width, pageRect.size.height, QImage::Format_ARGB32_Premultiplied);
110+
QImage renderImage(imageWidth, imageHeight, QImage::Format_ARGB32_Premultiplied);
111+
if (renderImage.isNull()) {
112+
CGColorSpaceRelease(genericColorSpace);
113+
return QImage();
114+
}
115+
116+
const uint32_t bitmapInfo = static_cast<uint32_t>(kCGImageAlphaPremultipliedFirst) |
117+
static_cast<uint32_t>(kCGBitmapByteOrder32Little);
67118

68119
CGContextRef bitmapContext = CGBitmapContextCreate(renderImage.scanLine(0),
69-
pageRect.size.width,
70-
pageRect.size.height,
120+
imageWidth,
121+
imageHeight,
71122
8, renderImage.bytesPerLine(),
72123
genericColorSpace,
73-
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little // may need to be changed to kCGBitmapByteOrder32Big
124+
bitmapInfo // may need to be changed to kCGBitmapByteOrder32Big
74125
);
126+
if (bitmapContext == nullptr) {
127+
CGColorSpaceRelease(genericColorSpace);
128+
return QImage();
129+
}
75130

76131
CGContextSetInterpolationQuality(bitmapContext, kCGInterpolationHigh);
77132
CGContextSetRenderingIntent(bitmapContext, kCGRenderingIntentDefault);
78133
CGContextSetRGBFillColor(bitmapContext, 1.0, 1.0, 1.0, 1.0);
79134
CGContextFillRect(bitmapContext, CGContextGetClipBoundingBox(bitmapContext));
80135

81-
// CGContextTranslateCTM( bitmapContext, 0, pageRect.size.height );
82-
// CGContextScaleCTM( bitmapContext, 1.0, -1.0 );
83-
84-
CGContextConcatCTM(bitmapContext, CGAffineTransformMakeScale(pdfScale, pdfScale));
85-
86-
/*CGAffineTransform pdfXfm = CGPDFPageGetDrawingTransform( page, kCGPDFMediaBox, CGRectMake(pageRect.origin.x, pageRect.origin.y, pageRect.size.width, pageRect.size.height) , 0, true );
87-
*/
88-
// CGContextConcatCTM( bitmapContext, pdfXfm );
89-
90-
CGContextDrawPDFPage(bitmapContext, page);
91-
92-
// CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
93-
94-
// QImage qtImage;
95-
96-
// CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(image));
97-
98-
/*lastPageData = (void *)dataRef;
99-
100-
if(!lastPageData)
101-
{
102-
QLOG_ERROR() << "Unable to extract image from PDF file using CGPDFDocument";
103-
CGImageRelease(image);
104-
CGContextRelease(bitmapContext);
105-
CGColorSpaceRelease(genericColorSpace);
106-
return QImage();
107-
}
108-
109-
const uchar *bytes = (const uchar *)CFDataGetBytePtr(dataRef);
136+
CGContextSaveGState(bitmapContext);
137+
CGContextScaleCTM(bitmapContext, pdfScale, pdfScale);
138+
[page drawWithBox:displayBox toContext:bitmapContext];
139+
CGContextRestoreGState(bitmapContext);
110140

111-
qtImage = QImage(bytes, pageRect.size.width, pageRect.size.height, QImage::Format_ARGB32);
112-
*/
113-
// CGImageRelease(image);
114-
// CFRelease(dataRef);
115141
CGContextRelease(bitmapContext);
116-
// CGPDFPageRelease(page);
117142
CGColorSpaceRelease(genericColorSpace);
118143

119-
// return qtImage;
120144
return renderImage;
121145
}
122146

custom_widgets/whats_new_dialog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent)
6363
" &#8226; Add an application language setting with a system default option in YACReader and YACReaderLibrary<br/>"
6464
" &#8226; Fix fullscreen mode in Windows, interaction with the OS is now possible while the apps are in fullscreen<br/>"
6565
" &#8226; Improve support for multi-screen setups<br/>"
66+
" &#8226; Fix PDFs with crop information on macOS<br/>"
6667
"<br/>"
6768
"<span style=\"font-weight:600\">All apps</span><br/>"
6869
" &#8226; Add support for user-installed Qt image format plugins via the shared <i>plugins/imageformats</i> folder in the YACReader settings directory<br/>"

0 commit comments

Comments
 (0)