// // KMPDFMorePagePrintManager.m // PDF Reader Pro Edition // // Created by 丁林圭 on 2018/5/17. // #import "KMPDFMultipleManager.h" #pragma mark - KMPDFMultiple @interface KMEachPageProperties() @end @implementation KMEachPageProperties -(void)dealloc { } @end #pragma mark - KMPDFMultipleManager @interface KMPDFMultipleManager() @end @implementation KMPDFMultipleManager - (void)dealloc { } #pragma mark Private Action - (void)PDFKitDrawPageToContext:(CGContextRef)context eachPages:(NSArray *)pages multipleOption:(KMEachPageProperties *)properties { CGSize size = properties.pageSize; CGFloat width = size.width; CGFloat height =size.height; if (kKMPDFMultiplePageDirection_Landscape == properties.direction) { width = size.height; height = size.width; } CGFloat contextWidth = width- properties.edgeInsets.left - properties.edgeInsets.right;//实际画page的总宽度 CGFloat contextHeight = height - properties.edgeInsets.bottom - properties.edgeInsets.top;//实际画page的总高度 NSInteger columnCout = properties.columnCout; NSInteger rowCount = properties.rowCount; CGFloat columnSpace = properties.columnSpace; NSInteger rowSpace = properties.rowSpace; CGFloat drawPageMaxW = (contextWidth - (columnCout - 1) * columnSpace)/ columnCout; //page的能画的最大宽度 CGFloat drawPageMaxH = (contextHeight - (rowCount -1) * rowSpace) / rowCount;//page能画的最大高度 CGFloat scanl = 1.0; CGFloat xTransform = 0; CGFloat yTransform = 0; if (kKMPDFMultiplePageOrder_LongitudinalAntitone == properties.order || kKMPDFMultiplePageOrder_Longitudinal == properties.order) { rowCount = properties.columnCout; columnCout = properties.rowCount; } for (NSInteger i = 0; i < rowCount; i++) { for (NSInteger j = 0;j < columnCout; j++) { NSUInteger index = j + i * columnCout; if (index >= pages.count) { break; } PDFPage *drawPage = pages[index]; NSRect pageBounds = [drawPage boundsForBox:kPDFDisplayBoxCropBox]; CGFloat orgPageWidth = pageBounds.size.width;//原始page的宽度(不管有没有旋转,都是旋转度数等于0) CGFloat orgPageHeight = pageBounds.size.height;//原始page的高度 NSMutableArray *annotations = [NSMutableArray array]; for (PDFAnnotation *annotation in drawPage.annotations) { [annotations addObject:annotation]; } if (kKMPDFCommentsForms_Documents == properties.commentsForms) { for (PDFAnnotation *annotation in annotations) { [annotation.page removeAnnotation:annotation]; } } else if (kKMPDFCommentsForms_DocumentStamps == properties.commentsForms) { for (PDFAnnotation *annotation in annotations) { if (![annotation isKindOfClass:[PDFAnnotationStamp class]]) { [annotation.page removeAnnotation:annotation]; } } } NSInteger rotate = drawPage.rotation; if (90 == rotate || 270 == rotate) { orgPageWidth = pageBounds.size.height; orgPageHeight = pageBounds.size.width; } scanl = MIN(drawPageMaxW/orgPageWidth,drawPageMaxH/orgPageHeight); //取出page横竖时能显示的最大Rect if (properties.isAutoRotate) { CGFloat tScanl1 = MIN(drawPageMaxW/pageBounds.size.width,drawPageMaxH/pageBounds.size.height);//page旋转度数为0度或者180度时,在指定的大小内,能显示的比例 CGFloat tScanl2 = MIN(drawPageMaxW/pageBounds.size.height,drawPageMaxH/pageBounds.size.width);//page旋转度数为90度或者270度时,在指定的大小内,能显示的比例 scanl = MAX(tScanl1, tScanl2); if (tScanl1 > tScanl2) { //高为竖直排列时,显示最大,则需将page时90度或者270度需要进行旋转 if (rotate == 90 || rotate == 270) { [drawPage setRotation:rotate - 90]; } } else { //宽为竖直排列时,显示最大,则需将page时0度或者180度需要进行旋转 if (rotate == 0 || rotate == 180) { [drawPage setRotation:rotate - 90]; } } if ( 90 == drawPage.rotation || 270 == drawPage.rotation) { orgPageWidth = pageBounds.size.height; orgPageHeight = pageBounds.size.width; } else { orgPageWidth = pageBounds.size.width; orgPageHeight = pageBounds.size.height; } } if (scanl >1) { scanl = 1; } //每一页都是它居中; CGFloat spaceX = (drawPageMaxW - orgPageWidth * scanl)/2; CGFloat spaceY = (drawPageMaxH - orgPageHeight* scanl)/2; if (kKMPDFMultiplePageOrder_Transverse == properties.order) { xTransform = (drawPageMaxW + columnSpace) * j + properties.edgeInsets.left + spaceX; //页面内容高度 + 下边的行间距 - 第几个cell的高度 +居中 yTransform = contextHeight + properties.edgeInsets.bottom - (drawPageMaxH + rowSpace) * (i+1) + spaceY + rowSpace; } else if (kKMPDFMultiplePageOrder_TransverseAntitone == properties.order) { xTransform = contextWidth + properties.edgeInsets.left - (drawPageMaxW + columnSpace) * (j + 1) + spaceX + columnSpace; yTransform = contextHeight - (drawPageMaxH + rowSpace) * (i+1) + spaceY + properties.edgeInsets.bottom + rowSpace; } else if (kKMPDFMultiplePageOrder_Longitudinal == properties.order) { xTransform = (drawPageMaxW + columnSpace) * i + properties.edgeInsets.left + spaceX; yTransform = contextHeight - (drawPageMaxH + rowSpace) * (j+1) + spaceY + properties.edgeInsets.bottom + rowSpace; } else if (kKMPDFMultiplePageOrder_LongitudinalAntitone == properties.order) { xTransform = contextWidth + properties.edgeInsets.left - (drawPageMaxW + columnSpace) * (i + 1) + spaceX + columnSpace; yTransform = contextHeight - (drawPageMaxH + rowSpace) * (j+1) + spaceY + properties.edgeInsets.bottom + rowSpace; } [NSGraphicsContext saveGraphicsState]; //平移 CGContextTranslateCTM(context, xTransform,yTransform); //缩放 CGContextScaleCTM(context, scanl, scanl); if (@available(macOS 10.12, *)) { [drawPage drawWithBox:kPDFDisplayBoxCropBox toContext:context]; [drawPage transformContext:context forBox:kPDFDisplayBoxCropBox]; } else { [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]]; [drawPage drawWithBox:kPDFDisplayBoxCropBox]; [NSGraphicsContext restoreGraphicsState]; [drawPage transformContextForBox:kPDFDisplayBoxCropBox]; } if (properties.isHasBorder) { CGFloat borderWidth = pageBounds.size.width; CGFloat borderHeight = pageBounds.size.height; if ( 90 == drawPage.rotation || 270 == drawPage.rotation) { borderWidth = pageBounds.size.height; borderWidth = pageBounds.size.width; } CGRect dirtyRect = CGRectMake(pageBounds.origin.x, pageBounds.origin.y, borderWidth, borderHeight); CGContextAddRect(context, dirtyRect); CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0); CGContextStrokePath(context); } [NSGraphicsContext restoreGraphicsState]; } } } - (void)CGDrawPageToContext:(CGContextRef)context eachPages:(NSArray *)pages multipleOption:(KMEachPageProperties *)properties { CGSize size = properties.pageSize; CGFloat width = size.width; CGFloat height =size.height; if (kKMPDFMultiplePageDirection_Landscape == properties.direction) { width = size.height; height = size.width; } CGFloat contextWidth = width- properties.edgeInsets.left - properties.edgeInsets.right; CGFloat contextHeight = height - properties.edgeInsets.bottom - properties.edgeInsets.top; NSInteger columnCout = properties.columnCout; NSInteger rowCount = properties.rowCount; CGFloat columnSpace = properties.columnSpace; NSInteger rowSpace = properties.rowSpace; CGFloat drawPageW = (contextWidth - (columnCout - 1) * columnSpace)/ columnCout; //画page的宽度 CGFloat drawPageH = (contextHeight - (rowCount -1) * rowSpace) / rowCount;//画page的高度 CGFloat scanl = 1.0; CGFloat xTransform = 0; CGFloat yTransform = 0; if (kKMPDFMultiplePageOrder_LongitudinalAntitone == properties.order || kKMPDFMultiplePageOrder_Longitudinal == properties.order) { rowCount = properties.columnCout; columnCout = properties.rowCount; } for (NSInteger i = 0; i < rowCount; i++) { for (NSInteger j = 0;j < columnCout; j++) { NSUInteger index = j + i * columnCout; if (index >= pages.count) { break; } PDFPage *drawPage = pages[index]; NSRect pageBounds = [drawPage boundsForBox:kPDFDisplayBoxCropBox]; CGFloat orgPageWidth = pageBounds.size.width;//原始page的宽度 CGFloat orgPageHeight = pageBounds.size.height;//原始page的高度 NSInteger rotate = drawPage.rotation; if (90 == rotate || 270 == rotate) { orgPageWidth = pageBounds.size.height; orgPageHeight = pageBounds.size.width; } if (properties.isAutoRotate) { CGFloat tScanl1 = MIN(drawPageW/pageBounds.size.width,drawPageH/pageBounds.size.height); CGFloat tScanl2 = MIN(drawPageW/pageBounds.size.height,drawPageH/pageBounds.size.width); scanl = MAX(tScanl1, tScanl2); if (tScanl1 > tScanl2) { if (rotate == 90 || rotate == 270) { rotate = rotate - 90; } } else { if (rotate != 90 || rotate != 270) { rotate = rotate - 90; } } if (rotate == -90) { rotate = 270; } } if ( 90 == rotate || 270 == rotate) { orgPageWidth = pageBounds.size.height; orgPageHeight = pageBounds.size.width; } else { orgPageWidth = pageBounds.size.width; orgPageHeight = pageBounds.size.height; } scanl = MIN(drawPageW/orgPageWidth,drawPageH/orgPageHeight); if (scanl >1) { scanl = 1; } // //每一页都是它居中; CGFloat spaceX = (drawPageW - orgPageWidth * scanl)/2; CGFloat spaceY = (drawPageH - orgPageHeight* scanl)/2; CGFloat offsetX = 0; CGFloat offsetY = 0; if (kKMPDFMultiplePageOrder_Transverse == properties.order) { if (rotate == 270) { offsetX = (properties.edgeInsets.bottom+ spaceY)/scanl; offsetY = - pageBounds.size.height - (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX + (drawPageH + rowSpace)/scanl * (rowCount - i -1) ; yTransform = offsetY - (drawPageW + columnSpace)/scanl * j; } else if (rotate == 180) { offsetX = -(pageBounds.size.width + properties.edgeInsets.left/scanl + spaceX/scanl); offsetY = -(pageBounds.size.height + properties.edgeInsets.bottom/scanl + spaceY/scanl); xTransform = offsetX - (drawPageW + columnSpace)/scanl * j ; yTransform = offsetY - (drawPageH + rowSpace)/scanl * (rowCount - i -1); } else if (rotate == 90) { offsetX = - (pageBounds.size.width + (properties.edgeInsets.bottom+ spaceY)/scanl); offsetY = (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX - (drawPageH + rowSpace)/scanl * (rowCount - i -1) ; yTransform = offsetY + (drawPageW + columnSpace)/scanl * j; } else { offsetX = (properties.edgeInsets.left + spaceX)/scanl; offsetY = (properties.edgeInsets.bottom+spaceY)/scanl; xTransform = offsetX + (drawPageW + columnSpace)/scanl * j ; yTransform = (offsetY + (drawPageH + rowSpace)/scanl * (rowCount - i -1)); } } else if (kKMPDFMultiplePageOrder_TransverseAntitone == properties.order) { if (rotate == 270) { offsetX = (properties.edgeInsets.bottom+ spaceY)/scanl; offsetY = - pageBounds.size.height - (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX + (drawPageH + rowSpace)/scanl * (rowCount - i -1) ; yTransform = offsetY - (drawPageW + columnSpace)/scanl * (columnCout - 1 - j); } else if (rotate == 180) { offsetX = -(pageBounds.size.width + properties.edgeInsets.left/scanl + spaceX/scanl); offsetY = -(pageBounds.size.height + properties.edgeInsets.bottom/scanl + spaceY/scanl); xTransform = offsetX - (drawPageW + columnSpace)/scanl * (columnCout - 1 - j) ; yTransform = offsetY - (drawPageH + rowSpace)/scanl * (rowCount - i -1); } else if (rotate == 90) { offsetX = - (pageBounds.size.width + (properties.edgeInsets.bottom+ spaceY)/scanl); offsetY = (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX - (drawPageH + rowSpace)/scanl * (rowCount - i -1) ; yTransform = offsetY + (drawPageW + columnSpace)/scanl * (columnCout - 1 - j); } else { offsetX = (properties.edgeInsets.left + spaceX)/scanl; offsetY = (properties.edgeInsets.bottom+spaceY)/scanl; xTransform = offsetX + (drawPageW + columnSpace)/scanl * (columnCout - 1 - j) ; yTransform = (offsetY + (drawPageH + rowSpace)/scanl * (rowCount - i -1)); } } else if (kKMPDFMultiplePageOrder_Longitudinal == properties.order) { if (rotate == 270) { offsetX = (properties.edgeInsets.bottom+ spaceY)/scanl; offsetY = - pageBounds.size.height - (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX + (drawPageH + rowSpace)/scanl * (rowCount - j -1) ; yTransform = offsetY - (drawPageW + columnSpace)/scanl * i; } else if (rotate == 180) { offsetX = -(pageBounds.size.width + properties.edgeInsets.left/scanl + spaceX/scanl); offsetY = -(pageBounds.size.height + properties.edgeInsets.bottom/scanl + spaceY/scanl); xTransform = offsetX - (drawPageW + columnSpace)/scanl * i ; yTransform = offsetY - (drawPageH + rowSpace)/scanl * (rowCount - j -1); } else if (rotate == 90) { offsetX = - (pageBounds.size.width + (properties.edgeInsets.bottom+ spaceY)/scanl); offsetY = (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX - (drawPageH + rowSpace)/scanl * (rowCount - j -1) ; yTransform = offsetY + (drawPageW + columnSpace)/scanl * i; } else { offsetX = (properties.edgeInsets.left + spaceX)/scanl; offsetY = (properties.edgeInsets.bottom+spaceY)/scanl; xTransform = offsetX + (drawPageW + columnSpace)/scanl * i ; yTransform = (offsetY + (drawPageH + rowSpace)/scanl * (rowCount - j -1)); } } else if (kKMPDFMultiplePageOrder_LongitudinalAntitone == properties.order) { if (rotate == 270) { offsetX = (properties.edgeInsets.bottom+ spaceY)/scanl; offsetY = - pageBounds.size.height - (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX + (drawPageH + rowSpace)/scanl * (rowCount - j -1) ; yTransform = offsetY - (drawPageW + columnSpace)/scanl * (columnCout - 1 - i); } else if (rotate == 180) { offsetX = -(pageBounds.size.width + properties.edgeInsets.left/scanl + spaceX/scanl); offsetY = -(pageBounds.size.height + properties.edgeInsets.bottom/scanl + spaceY/scanl); xTransform = offsetX - (drawPageW + columnSpace)/scanl * (columnCout - 1 - i) ; yTransform = offsetY - (drawPageH + rowSpace)/scanl * (rowCount - j -1); } else if (rotate == 90) { offsetX = - (pageBounds.size.width + (properties.edgeInsets.bottom+ spaceY)/scanl); offsetY = (properties.edgeInsets.left + spaceX)/scanl; xTransform = offsetX - (drawPageH + rowSpace)/scanl * (rowCount - j -1) ; yTransform = offsetY + (drawPageW + columnSpace)/scanl * (columnCout - 1 - i); } else { offsetX = (properties.edgeInsets.left + spaceX)/scanl; offsetY = (properties.edgeInsets.bottom+spaceY)/scanl; xTransform = offsetX + (drawPageW + columnSpace)/scanl * (columnCout - 1 - i) ; yTransform = (offsetY + (drawPageH + rowSpace)/scanl * (rowCount - j -1)); } } [NSGraphicsContext saveGraphicsState]; //缩放 CGContextScaleCTM(context, scanl, scanl); CGContextRotateCTM(context, -rotate*M_PI/180); CGContextTranslateCTM(context,xTransform,yTransform); CGContextDrawPDFPage(context,drawPage.pageRef); if (properties.isHasBorder) { CGFloat borderWidth = pageBounds.size.width; CGFloat borderHeight = pageBounds.size.height; CGRect dirtyRect = CGRectMake(0, 0, borderWidth, borderHeight); CGContextAddRect(context, dirtyRect); CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0); CGContextStrokePath(context); } [NSGraphicsContext restoreGraphicsState]; } } } #pragma mark Publick Action + (KMPDFMultipleManager *)defaultManager { static KMPDFMultipleManager *singleton = nil; static dispatch_once_t pred; dispatch_once(&pred, ^{ singleton = [[KMPDFMultipleManager alloc] init]; }); return singleton; } - (void)savePDFDocumentPath:(NSString *)path drawPages:(NSArray *)pages multipleOption:(KMEachPageProperties *)properties completionHandler:(void (^)(BOOL isSuccessfully))handler { CFStringRef filePath = (__bridge CFStringRef)path; CFURLRef url = CFURLCreateWithFileSystemPath(NULL, filePath, kCFURLPOSIXPathStyle, 0); CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("PDF Reader Pro")); CGContextRef pdfContext = CGPDFContextCreateWithURL(url, &CGRectZero, myDictionary); CFRelease(myDictionary); CFRelease(url); if (!pdfContext) { handler(NO); return; } [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:pdfContext flipped:NO]]; NSInteger eachPageCount = (properties.columnCout * properties.rowCount); NSInteger numPages = 0; if (pages.count % eachPageCount == 0) { numPages = pages.count / eachPageCount; } else { numPages = pages.count / eachPageCount +1; } for (NSInteger index = 0; index < numPages; index ++) { NSMutableArray * eachPages = [NSMutableArray array]; for (NSInteger i = 0; i= pages.count) { break; } PDFPage * page = [pages[currentIndex] copy]; [eachPages addObject:page]; } if (eachPages.count < 1) { break; } CGSize size = properties.pageSize; CGFloat width = size.width; CGFloat height =size.height; if (kKMPDFMultiplePageDirection_Landscape == properties.direction) { width = size.height; height = size.width; } CGRect mediaBox = CGRectMake(0, 0, width,height); CGContextBeginPage(pdfContext, &mediaBox); [self drawPageToContext:pdfContext eachPages:eachPages multipleOption:properties]; CGContextEndPage(pdfContext); } CGPDFContextClose(pdfContext); CGContextRelease (pdfContext); handler(YES); } - (void)drawPageToContext:(CGContextRef)context eachPages:(NSArray *)pages multipleOption:(KMEachPageProperties *)properties { /**** *可以统一使用CG类来进行缩放、平移、旋转 *PDFkit 自带画page的方法,这样能够保留注释存在 if (@available(macOS 10.12, *)) { [self PDFKitDrawPageToContext:context eachPages:pages multipleOption:properties]; } else { [self CGDrawPageToContext:context eachPages:pages multipleOption:properties]; } *****/ [self PDFKitDrawPageToContext:context eachPages:pages multipleOption:properties]; } @end