ReaderThumbRender.m 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //
  2. // ReaderThumbRender.m
  3. // Reader v2.8.6
  4. //
  5. // Created by Julius Oklamcak on 2011-09-01.
  6. // Copyright © 2011-2015 Julius Oklamcak. All rights reserved.
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a copy
  9. // of this software and associated documentation files (the "Software"), to deal
  10. // in the Software without restriction, including without limitation the rights to
  11. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  12. // of the Software, and to permit persons to whom the Software is furnished to
  13. // do so, subject to the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included in all
  16. // copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  19. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  23. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. //
  25. #import "ReaderThumbRender.h"
  26. #import "ReaderThumbCache.h"
  27. #import "ReaderThumbView.h"
  28. #import "CGPDFDocument.h"
  29. #import <ImageIO/ImageIO.h>
  30. @implementation ReaderThumbRender
  31. {
  32. ReaderThumbRequest *request;
  33. }
  34. #pragma mark - ReaderThumbRender instance methods
  35. - (instancetype)initWithRequest:(ReaderThumbRequest *)options
  36. {
  37. if ((self = [super initWithGUID:options.guid]))
  38. {
  39. request = options;
  40. }
  41. return self;
  42. }
  43. - (void)cancel
  44. {
  45. [super cancel]; // Cancel the operation
  46. request.thumbView.operation = nil; // Break retain loop
  47. request.thumbView = nil; // Release target thumb view on cancel
  48. [[ReaderThumbCache sharedInstance] removeNullForKey:request.cacheKey];
  49. }
  50. - (NSURL *)thumbFileURL
  51. {
  52. NSFileManager *fileManager = [NSFileManager new]; // File manager instance
  53. NSString *cachePath = [ReaderThumbCache thumbCachePathForGUID:request.guid]; // Thumb cache path
  54. [fileManager createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:NULL];
  55. NSString *fileName = [[NSString alloc] initWithFormat:@"%@.png", request.thumbName]; // Thumb file name
  56. return [NSURL fileURLWithPath:[cachePath stringByAppendingPathComponent:fileName]]; // File URL
  57. }
  58. - (void)main
  59. {
  60. NSInteger page = request.thumbPage; NSString *password = request.password;
  61. CGImageRef imageRef = NULL; CFURLRef fileURL = (__bridge CFURLRef)request.fileURL;
  62. CGPDFDocumentRef thePDFDocRef = CGPDFDocumentCreateUsingUrl(fileURL, password);
  63. if (thePDFDocRef != NULL) // Check for non-NULL CGPDFDocumentRef
  64. {
  65. CGPDFPageRef thePDFPageRef = CGPDFDocumentGetPage(thePDFDocRef, page);
  66. if (thePDFPageRef != NULL) // Check for non-NULL CGPDFPageRef
  67. {
  68. CGFloat thumb_w = request.thumbSize.width; // Maximum thumb width
  69. CGFloat thumb_h = request.thumbSize.height; // Maximum thumb height
  70. CGRect cropBoxRect = CGPDFPageGetBoxRect(thePDFPageRef, kCGPDFCropBox);
  71. CGRect mediaBoxRect = CGPDFPageGetBoxRect(thePDFPageRef, kCGPDFMediaBox);
  72. CGRect effectiveRect = CGRectIntersection(cropBoxRect, mediaBoxRect);
  73. NSInteger pageRotate = CGPDFPageGetRotationAngle(thePDFPageRef); // Angle
  74. CGFloat page_w = 0.0f; CGFloat page_h = 0.0f; // Rotated page size
  75. switch (pageRotate) // Page rotation (in degrees)
  76. {
  77. default: // Default case
  78. case 0: case 180: // 0 and 180 degrees
  79. {
  80. page_w = effectiveRect.size.width;
  81. page_h = effectiveRect.size.height;
  82. break;
  83. }
  84. case 90: case 270: // 90 and 270 degrees
  85. {
  86. page_h = effectiveRect.size.width;
  87. page_w = effectiveRect.size.height;
  88. break;
  89. }
  90. }
  91. CGFloat scale_w = (thumb_w / page_w); // Width scale
  92. CGFloat scale_h = (thumb_h / page_h); // Height scale
  93. CGFloat scale = 0.0f; // Page to target thumb size scale
  94. if (page_h > page_w)
  95. scale = ((thumb_h > thumb_w) ? scale_w : scale_h); // Portrait
  96. else
  97. scale = ((thumb_h < thumb_w) ? scale_h : scale_w); // Landscape
  98. NSInteger target_w = (page_w * scale); // Integer target thumb width
  99. NSInteger target_h = (page_h * scale); // Integer target thumb height
  100. if (target_w % 2) target_w--; if (target_h % 2) target_h--; // Even
  101. target_w *= request.scale; target_h *= request.scale; // Screen scale
  102. CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); // RGB color space
  103. CGBitmapInfo bmi = (kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
  104. CGContextRef context = CGBitmapContextCreate(NULL, target_w, target_h, 8, 0, rgb, bmi);
  105. if (context != NULL) // Must have a valid custom CGBitmap context to draw into
  106. {
  107. CGRect thumbRect = CGRectMake(0.0f, 0.0f, target_w, target_h); // Target thumb rect
  108. CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); CGContextFillRect(context, thumbRect); // White fill
  109. CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(thePDFPageRef, kCGPDFCropBox, thumbRect, 0, true)); // Fit rect
  110. //CGContextSetRenderingIntent(context, kCGRenderingIntentDefault); CGContextSetInterpolationQuality(context, kCGInterpolationDefault);
  111. CGContextDrawPDFPage(context, thePDFPageRef); // Render the PDF page into the custom CGBitmap context
  112. imageRef = CGBitmapContextCreateImage(context); // Create CGImage from custom CGBitmap context
  113. CGContextRelease(context); // Release custom CGBitmap context reference
  114. }
  115. CGColorSpaceRelease(rgb); // Release device RGB color space reference
  116. }
  117. CGPDFDocumentRelease(thePDFDocRef); // Release CGPDFDocumentRef reference
  118. }
  119. if (imageRef != NULL) // Create UIImage from CGImage and show it, then save thumb as PNG
  120. {
  121. UIImage *image = [UIImage imageWithCGImage:imageRef scale:request.scale orientation:UIImageOrientationUp];
  122. [[ReaderThumbCache sharedInstance] setObject:image forKey:request.cacheKey]; // Update cache
  123. if (self.isCancelled == NO) // Show the image in the target thumb view on the main thread
  124. {
  125. ReaderThumbView *thumbView = request.thumbView; // Target thumb view for image show
  126. NSUInteger targetTag = request.targetTag; // Target reference tag for image show
  127. dispatch_async(dispatch_get_main_queue(), // Queue image show on main thread
  128. ^{
  129. if (thumbView.targetTag == targetTag) [thumbView showImage:image];
  130. });
  131. }
  132. CFURLRef thumbURL = (__bridge CFURLRef)[self thumbFileURL]; // Thumb cache path with PNG file name URL
  133. CGImageDestinationRef thumbRef = CGImageDestinationCreateWithURL(thumbURL, (CFStringRef)@"public.png", 1, NULL);
  134. if (thumbRef != NULL) // Write the thumb image file out to the thumb cache directory
  135. {
  136. CGImageDestinationAddImage(thumbRef, imageRef, NULL); // Add the image
  137. CGImageDestinationFinalize(thumbRef); // Finalize the image file
  138. CFRelease(thumbRef); // Release CGImageDestination reference
  139. }
  140. CGImageRelease(imageRef); // Release CGImage reference
  141. }
  142. else // No image - so remove the placeholder object from the cache
  143. {
  144. [[ReaderThumbCache sharedInstance] removeNullForKey:request.cacheKey];
  145. }
  146. request.thumbView.operation = nil; // Break retain loop
  147. }
  148. @end