// // KMPosterPrintManager.m // PDF Reader Pro Edition // // Created by 丁林圭 on 2018/4/3. // #import "KMPosterPrintManager.h" #pragma mark - KMPDFPosterPrint @implementation KMPDFPosterPrint - (void)dealloc { } - (id)copyWithZone:(NSZone *)zone { KMPDFPosterPrint *print = [[[self class] allocWithZone:zone] init]; print.isLabel = self.isLabel; print.isCut = self.isLabel; print.direction = self.direction; print.cropPages = self.cropPages; print.scale = self.scale; print.overlap = self.overlap; return print; } @end @interface KMPosterPrintManager() @property (nonatomic,retain) KMPDFPosterPrint * PDFPrint; @property (nonatomic,retain) NSArray * hourArray; @property (nonatomic,retain) NSArray * vertArray; @property (nonatomic,retain) NSString * filePath; @property (assign) NSUInteger lineIndex;//记录水平第几行 @property (assign) NSUInteger columnIndex;//记录竖直第几列 @property (assign) CGRect currentPageRect; @end @implementation KMPosterPrintManager -(void)dealloc { } #pragma mark - Private method -(void)splitPage:(PDFPage *)page rect:(CGRect)newRect toContext:(CGContextRef)pdfContext { PDFPage * newPage = page.copy; NSMutableArray *annotations = [NSMutableArray array]; for (PDFAnnotation *annotation in newPage.annotations) { [annotations addObject:annotation]; } if (kKMPDFPosterCommentsForms_Documents == _PDFPrint.commentsForms) { for (PDFAnnotation *annotation in annotations) { [annotation.page removeAnnotation:annotation]; } } else if (kKMPDFPosterCommentsForms_DocumentStamps == _PDFPrint.commentsForms) { for (PDFAnnotation *annotation in annotations) { if (![annotation isKindOfClass:[PDFAnnotationStamp class]]) { [annotation.page removeAnnotation:annotation]; } } } [newPage setBounds:newRect forBox:kPDFDisplayBoxCropBox]; [self drawPDFPage:newPage cropRect:newRect toContext:pdfContext]; } //将裁剪到的页面补到A4纸面上去 -(void)drawPDFPage:(PDFPage *)page cropRect:(CGRect )cropRect toContext:(CGContextRef)context; { CGRect mediaBox = CGRectZero; CGFloat KBlankA4W =0; CGFloat KBlankA4H =0; if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { if (self.PDFPrint.isOriginalPageSize) { if (page.rotation == 90 || page.rotation == 270) { KBlankA4W = cropRect.size.height; KBlankA4H = cropRect.size.width; } else { KBlankA4W = cropRect.size.width; KBlankA4H = cropRect.size.height; } } else { if(kKMPDFPosterPrintDirection_Portrait == self.PDFPrint.direction){ KBlankA4W = self.PDFPrint.fullPageSize.width; KBlankA4H = self.PDFPrint.fullPageSize.height; } else { KBlankA4W = self.PDFPrint.fullPageSize.height; KBlankA4H = self.PDFPrint.fullPageSize.width; } } } else { if(kKMPDFPosterPrintDirection_Portrait == self.PDFPrint.direction){ KBlankA4W = self.PDFPrint.splitSize.width; KBlankA4H = self.PDFPrint.splitSize.height; } else { KBlankA4W = self.PDFPrint.splitSize.height; KBlankA4H = self.PDFPrint.splitSize.width; } } mediaBox = CGRectMake(0, 0, KBlankA4W, KBlankA4H); CGContextBeginPage(context, &mediaBox); [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]]; if (self.PDFPrint.isCut) { [self drawCutMarkContext:context contextSize:CGSizeMake(KBlankA4W, KBlankA4H)]; } if (self.PDFPrint.isLabel){ [self drawLabelTextContextSize:CGSizeMake(KBlankA4W, KBlankA4H)]; } NSInteger rotate = page.rotation; CGFloat PDFWidth = cropRect.size.width; CGFloat PDFHeight = cropRect.size.height; if (90 == rotate || 270 == rotate) { PDFWidth = cropRect.size.height; PDFHeight = cropRect.size.width; } CGFloat scanl = self.PDFPrint.scale; CGFloat left = self.PDFPrint.edgeInsets.left; CGFloat right = self.PDFPrint.edgeInsets.right; CGFloat top = self.PDFPrint.edgeInsets.top; CGFloat bottom = self.PDFPrint.edgeInsets.bottom; if (kKMPDFPosterSplitType_PageNumber == self.PDFPrint.splitType) { left = self.PDFPrint.fullPageEdgeInsets.left; right = self.PDFPrint.fullPageEdgeInsets.right; top = self.PDFPrint.fullPageEdgeInsets.top; bottom = self.PDFPrint.fullPageEdgeInsets.bottom; CGFloat contextA4W = KBlankA4W - left - right;//实际内容的宽度 CGFloat contextA4H = KBlankA4H - top - bottom;//实际内容的高度 scanl = MIN(contextA4W/PDFWidth, contextA4H/PDFHeight); } CGFloat xTransform = 0; CGFloat yTransform = 0; if (rotate == 90) { if (self.hourArray.count == 1 ) { xTransform = (mediaBox.size.width - PDFWidth *scanl)/2; } else { if (cropRect.size.height - cropRect.origin.y > 0) { xTransform = mediaBox.size.width - PDFWidth * scanl - right; } else { xTransform = left; } } if (self.vertArray.count == 1) { yTransform = (mediaBox.size.height - PDFHeight * scanl)/2; } else { if (cropRect.size.width - cropRect.origin.x > 0) { yTransform = bottom; } else { yTransform = mediaBox.size.height - PDFHeight * scanl - top; } } } else if (rotate == 180) { if (self.hourArray.count == 1 ) { xTransform = (mediaBox.size.width - PDFWidth * scanl)/2; } else { if (cropRect.origin.x > 0) { xTransform = mediaBox.size.width - PDFWidth * scanl -right; } else { xTransform = left; } } if (self.vertArray.count == 1) { yTransform = (mediaBox.size.height - PDFHeight * scanl)/2; } else { if (cropRect.origin.y > 0) { yTransform = mediaBox.size.height - PDFHeight * scanl - top; } else { yTransform = bottom; } } } else if (rotate == 270) { if (self.hourArray.count == 1 ) { xTransform = (mediaBox.size.width - PDFWidth * scanl)/2; } else { if (cropRect.size.height - cropRect.origin.y > 0) { xTransform = left; } else { xTransform = mediaBox.size.width - PDFWidth * scanl - right; } } if (self.vertArray.count == 1) { yTransform = (mediaBox.size.height - PDFHeight * scanl)/2; } else { if (cropRect.size.width - cropRect.origin.x > 0) { yTransform = mediaBox.size.height - PDFHeight * scanl - top; } else { yTransform = bottom; } } } else { if (self.hourArray.count == 1 ) { xTransform = (mediaBox.size.width - PDFWidth * scanl)/2; } else { if (cropRect.origin.x > 0) { xTransform = left; } else { xTransform = mediaBox.size.width - PDFWidth * scanl - right; } } if (self.vertArray.count == 1) { yTransform = (mediaBox.size.height - PDFHeight * scanl)/2; } else { if (cropRect.origin.y > 0) { yTransform = bottom; } else { yTransform = mediaBox.size.height - PDFHeight * scanl - top; } } } CGContextTranslateCTM(context, xTransform,yTransform); CGContextScaleCTM(context, scanl, scanl); if (@available(macOS 10.12, *)) { [page drawWithBox:kPDFDisplayBoxCropBox toContext:context]; [page transformContext:context forBox:kPDFDisplayBoxCropBox]; } else { [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]]; [page drawWithBox:kPDFDisplayBoxCropBox]; [NSGraphicsContext restoreGraphicsState]; [page transformContextForBox:kPDFDisplayBoxCropBox]; } [NSGraphicsContext restoreGraphicsState]; CGContextEndPage(context); } -(void)drawCutMarkContext:(CGContextRef)context contextSize:(CGSize)size { CGContextSetStrokeColorWithColor(context, [NSColor blackColor].CGColor); CGContextSetLineWidth(context, 1.0); CGContextSetLineCap(context, kCGLineCapSquare); if(self.columnIndex == 1 && self.lineIndex == 1) { [self drawLeftBottomCutMarks:context size:size]; [self drawRightTopCutMarks:context size:size]; [self drawRightBottomCutMarks:context size:size]; } else if (self.lineIndex == 1 && self.columnIndex == self.vertArray.count) { [self drawLeftTopCutMarks:context size:size]; [self drawRightTopCutMarks:context size:size]; [self drawRightBottomCutMarks:context size:size]; } else if (self.columnIndex == 1 && self.lineIndex == self.hourArray.count) { [self drawLeftTopCutMarks:context size:size]; [self drawLeftBottomCutMarks:context size:size]; [self drawRightBottomCutMarks:context size:size]; } else if (self.columnIndex == self.vertArray.count && self.hourArray.count == self.lineIndex) { [self drawLeftTopCutMarks:context size:size]; [self drawLeftBottomCutMarks:context size:size]; [self drawRightTopCutMarks:context size:size]; } else { [self drawLeftTopCutMarks:context size:size]; [self drawLeftBottomCutMarks:context size:size]; [self drawRightTopCutMarks:context size:size]; [self drawRightBottomCutMarks:context size:size]; } //绘制完成 CGContextStrokePath(context); } //左上角切割标记 -(void)drawLeftTopCutMarks:(CGContextRef)context size:(CGSize)size { CGFloat KBlankA4H =size.height; CGPoint point_LeftTop = CGPointZero; if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { point_LeftTop.x = self.PDFPrint.fullPageEdgeInsets.left; } else { point_LeftTop.x = self.PDFPrint.edgeInsets.left; } point_LeftTop.y = KBlankA4H - self.PDFPrint.edgeInsets.top; CGContextMoveToPoint(context, 10,point_LeftTop.y); CGContextAddLineToPoint(context,point_LeftTop.x, point_LeftTop.y); CGContextMoveToPoint(context, point_LeftTop.x - 10,KBlankA4H -10); CGContextAddLineToPoint(context,point_LeftTop.x - 10,point_LeftTop.y); } //右上角切割标记 -(void)drawRightTopCutMarks:(CGContextRef)context size:(CGSize)size { CGFloat KBlankA4W =size.width; CGFloat KBlankA4H =size.height; CGPoint point_RightTop = CGPointZero;//右上角 if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { point_RightTop.x = KBlankA4W - self.PDFPrint.fullPageEdgeInsets.right; point_RightTop.y = KBlankA4H - self.PDFPrint.fullPageEdgeInsets.top; } else { point_RightTop.x = KBlankA4W - self.PDFPrint.edgeInsets.right; point_RightTop.y = KBlankA4H - self.PDFPrint.edgeInsets.top; } CGContextMoveToPoint(context,point_RightTop.x,point_RightTop.y); CGContextAddLineToPoint(context,KBlankA4W - 10,point_RightTop.y); CGContextMoveToPoint(context,point_RightTop.x + 10,KBlankA4H - 10); CGContextAddLineToPoint(context,point_RightTop.x + 10,point_RightTop.y); } //左下角切割标记 -(void)drawLeftBottomCutMarks:(CGContextRef)context size:(CGSize)size { CGPoint point_LeftBottom = CGPointZero;//左下角 if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { point_LeftBottom.x = self.PDFPrint.fullPageEdgeInsets.left; point_LeftBottom.y = self.PDFPrint.fullPageEdgeInsets.bottom; } else { point_LeftBottom.x = self.PDFPrint.edgeInsets.left; point_LeftBottom.y = self.PDFPrint.edgeInsets.bottom; } //左下角 CGContextMoveToPoint(context, 10,point_LeftBottom.y); CGContextAddLineToPoint(context,point_LeftBottom.x, point_LeftBottom.y); CGContextMoveToPoint(context, point_LeftBottom.x- 10,10); CGContextAddLineToPoint(context,point_LeftBottom.x - 10,point_LeftBottom.y); } //右下角切割标记 -(void)drawRightBottomCutMarks:(CGContextRef)context size:(CGSize)size { CGFloat KBlankA4W =size.width; CGPoint point_RightBottom = CGPointZero;//右下角 if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { point_RightBottom.x = KBlankA4W - self.PDFPrint.fullPageEdgeInsets.right; point_RightBottom.y = self.PDFPrint.fullPageEdgeInsets.bottom; } else { point_RightBottom.x = KBlankA4W - self.PDFPrint.edgeInsets.right; point_RightBottom.y = self.PDFPrint.edgeInsets.bottom; } CGContextMoveToPoint(context,KBlankA4W - 10,point_RightBottom.y); CGContextAddLineToPoint(context,point_RightBottom.x,point_RightBottom.y); CGContextMoveToPoint(context,point_RightBottom.x+ 10,10); CGContextAddLineToPoint(context,point_RightBottom.x+ 10,point_RightBottom.y); } - (void)drawLabelTextContextSize:(CGSize)contextSize { CGFloat KBlankA4W =contextSize.width; CGFloat KBlankA4H =contextSize.height; NSString *contextString = nil; if (self.PDFPrint.labelString && self.PDFPrint.labelString.length > 0) { contextString = self.PDFPrint.labelString; } else { NSDate *date = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"YYYY-MM-dd hh:mm:ss"]; contextString = [NSString stringWithFormat:@"(%ld,%ld) %@ %@",self.columnIndex,self.lineIndex,[self.filePath lastPathComponent],[formatter stringFromDate:date]]; } CGFloat fontSize = 12.0 * (MAX(KBlankA4W, KBlankA4H)/842); NSFont *font = [NSFont systemFontOfSize:fontSize]; NSColor *color = [NSColor blackColor]; NSSize size = NSZeroSize; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; [style setAlignment:NSCenterTextAlignment]; [style setLineBreakMode:NSLineBreakByCharWrapping]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:style forKey:NSParagraphStyleAttributeName]; [dictionary setObject:color forKey:NSForegroundColorAttributeName]; [dictionary setObject:font forKey:NSFontAttributeName]; size = [contextString boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:dictionary].size; if (self.PDFPrint.splitType == kKMPDFPosterSplitType_PageNumber) { [contextString drawInRect:CGRectMake(self.PDFPrint.fullPageEdgeInsets.left +10, KBlankA4H - self.PDFPrint.fullPageEdgeInsets.top + size.height, size.width, size.height) withAttributes:dictionary]; } else { [contextString drawInRect:CGRectMake(self.PDFPrint.edgeInsets.left +10, KBlankA4H - self.PDFPrint.edgeInsets.top + size.height, size.width, size.height) withAttributes:dictionary]; } } #pragma mark Publick Action + (KMPosterPrintManager *)defaultManager { static KMPosterPrintManager *singleton = nil; static dispatch_once_t pred; dispatch_once(&pred, ^{ singleton = [[KMPosterPrintManager alloc] init]; }); return singleton; } - (void)savePath:(NSString *)path posterPrint:(KMPDFPosterPrint *)print completionHandler:(void (^)(BOOL))complet { self.filePath = path; self.PDFPrint = print; NSArray * cropPages = print.cropPages; if (print.isReversePage) { cropPages = [[print.cropPages reverseObjectEnumerator] allObjects]; } 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) { complet(NO); return; } for (PDFPage * page in cropPages) { [self posterPrintPage:page posterPrint:print completionHandler:^(NSArray *horizontals, NSArray *verticals) { self.hourArray = horizontals; self.vertArray = [[verticals reverseObjectEnumerator] allObjects];//裁剪的时候,应从上自下依次裁剪打印 NSInteger rotate = page.rotation; if (90 == rotate || 270 == rotate) { self.hourArray = verticals; self.vertArray = [[horizontals reverseObjectEnumerator] allObjects]; } if (kKMPDFPosterSplitType_PageNumber == print.splitType) { for (NSUInteger j=0; j< _vertArray.count; j++) { self.columnIndex = j + 1; for (NSUInteger i=0; i< _hourArray.count; i++) { CGRect newRect = CGRectZero; self.lineIndex = i + 1; if (i != 0) { newRect.origin.x = [_hourArray[i - 1] floatValue] -print.overlap; } if (j + 1 != _vertArray.count) { newRect.origin.y = [_vertArray[j + 1] floatValue] -print.overlap; } newRect.size.width = [_hourArray[i] floatValue] - newRect.origin.x; newRect.size.height = [_vertArray[j] floatValue] - newRect.origin.y; [self splitPage:page rect:newRect toContext:pdfContext]; } } } else { for (NSUInteger j=0; j< _vertArray.count; j++) { self.columnIndex = j + 1; for (NSUInteger i=0; i< _hourArray.count; i++) { self.lineIndex = i + 1; CGRect newRect = CGRectZero; if (i != 0) { newRect.origin.x = [_hourArray[i - 1] floatValue] -print.overlap/print.scale; } if (j + 1 != _vertArray.count) { newRect.origin.y = [_vertArray[j + 1] floatValue] -print.overlap/print.scale; } newRect.size.width = [_hourArray[i] floatValue] - newRect.origin.x; newRect.size.height = [_vertArray[j] floatValue] - newRect.origin.y; [self splitPage:page rect:newRect toContext:pdfContext]; } } } }]; } CGPDFContextClose(pdfContext); CGContextRelease (pdfContext); complet(YES); } //得到裁剪点数组 - (void)posterPrintPage:(PDFPage *)page posterPrint:(KMPDFPosterPrint *)properties completionHandler:(postPrintCallback)handler { NSMutableArray *horizontalArray = [NSMutableArray array];//水平坐标数组 NSMutableArray *verticalArray = [NSMutableArray array];//垂直坐标数组 NSRect rect = [page boundsForBox:kPDFDisplayBoxCropBox]; CGFloat orgPDFWidth = rect.size.width;//待裁剪的Page宽度 CGFloat orgPDFHeight = rect.size.height;//待裁剪的Page高度 NSInteger rotate = page.rotation; if (90 == rotate || 270 == rotate) { orgPDFWidth = rect.size.height; orgPDFHeight = rect.size.width; } CGFloat overlap = properties.overlap; if (kKMPDFPosterSplitType_PageNumber == properties.splitType) { NSInteger lineCount = properties.lineCount; if (lineCount>0) { CGFloat splitPDFW = orgPDFWidth + (lineCount - 1) * overlap; CGFloat spaceW = splitPDFW/lineCount; //均分宽度 [horizontalArray addObject:@(spaceW)]; for (NSInteger m =1; m< lineCount; m ++) { [horizontalArray addObject:@(spaceW + m *(spaceW - overlap))]; } } NSInteger columnCount = properties.columnCount; if (columnCount>0) { CGFloat splitPDFH = orgPDFHeight + (columnCount- 1) * overlap; CGFloat spaceH = splitPDFH/columnCount; //均分高度 [verticalArray addObject:@(spaceH)]; for (NSInteger n =1; n< columnCount; n ++) { [verticalArray addObject:@(spaceH + n *(spaceH - overlap))]; } } } else { CGFloat scale = properties.scale; CGFloat splitWidth = properties.splitSize.width; CGFloat splitHeight = properties.splitSize.height; if (kKMPDFPosterPrintDirection_Landscape == properties.direction) { splitWidth = properties.splitSize.height; splitHeight = properties.splitSize.width; } CGFloat contextA4W = splitWidth - properties.edgeInsets.left - properties.edgeInsets.right;//实际内容的宽度 CGFloat contextA4H = splitHeight - properties.edgeInsets.bottom - properties.edgeInsets.top;//实际内容的高度 splitWidth = contextA4W/scale; splitHeight = contextA4H/scale; NSInteger columnCount = 0;//宽能切完整的个数 CGFloat postPrintX = (contextA4W - overlap)/scale; while ((splitWidth + columnCount * postPrintX)< orgPDFWidth ) { columnCount ++; } CGFloat totalWidth = orgPDFWidth + columnCount * overlap; CGFloat firstX = (totalWidth - (columnCount-1) * splitWidth)/ 2; //计算第一个横向切割点 //计算切割点 if (columnCount >0) { if (firstX > 0) { CGFloat ww = firstX; [horizontalArray addObject:@(ww)]; for (NSInteger i =1; i< columnCount; i ++) { [horizontalArray addObject:@(postPrintX *i + ww)]; } [horizontalArray addObject:@(orgPDFWidth)]; } else if(0 == (int)firstX){ [horizontalArray addObject:@(splitWidth)]; for (NSInteger j =1; j< columnCount; j ++) { [horizontalArray addObject:@(splitWidth + postPrintX *j)]; } } } else { [horizontalArray addObject:@(orgPDFWidth)]; } NSInteger lineCount = 0;//高能切完整的个数 CGFloat postPrintY = (contextA4H - overlap)/scale; while ((splitHeight + lineCount* postPrintY)< orgPDFHeight ) { lineCount ++; } CGFloat totalHeight = orgPDFHeight + lineCount * overlap; CGFloat firstY = (totalHeight - (lineCount-1) * splitHeight)/ 2; //计算第一个纵向的切割点 if (lineCount >0) { if (firstY > 0) { CGFloat hh = firstY; [verticalArray addObject:@(hh)]; for (NSInteger m =1; m< lineCount; m ++) { [verticalArray addObject:@(postPrintY *m + hh)]; } [verticalArray addObject:@(orgPDFHeight)]; } else if(0 == (int)firstY){ [verticalArray addObject:@(splitHeight)]; for (NSInteger n =1; n< lineCount; n++) { [verticalArray addObject:@(splitHeight + postPrintY *n)]; } } } else { [verticalArray addObject:@(orgPDFHeight)]; } } handler(horizontalArray,verticalArray); } @end