KMBookletManager.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. //
  2. // BLBookletManager.m
  3. // Booklet
  4. //
  5. // Created by 蒋志鹏 on 2018/5/3.
  6. // Copyright © 2018年 NO1. All rights reserved.
  7. //
  8. #import "KMBookletManager.h"
  9. #import "KMBookletParameterModel.h"
  10. #import "NSMutableArray+KMOddEvenPartFetch.h"
  11. NSString *const BLBlankPageCountKey = @"blankPageNum";//空白页面数量key
  12. NSString *const BLBookletInfoKey = @"bookletInfo";//小册子编号和页数信息key
  13. @interface KMBookletManager()
  14. @end
  15. @implementation KMBookletManager
  16. - (void)dealloc
  17. {
  18. }
  19. //单例方法
  20. static KMBookletManager *manager = nil;
  21. + (instancetype)sharedManager
  22. {
  23. static dispatch_once_t onceToken;
  24. dispatch_once(&onceToken, ^{
  25. manager = [[KMBookletManager alloc] init];
  26. });
  27. return manager;
  28. }
  29. //根据参数,生成PDF并回调
  30. - (void)generateNewPDFWithParameterModel:(KMBookletParameterModel *)parameterModel
  31. completionHandler:(Booklet_GenerateNewPDFBlock)handle
  32. {
  33. //获取数组,如果数组为空,回调错误,不处理
  34. NSArray *printArr = [self fetchDrawPageArraysWithModel:parameterModel];
  35. if (printArr.count == 0) {
  36. if (handle) {
  37. handle(NO,parameterModel.savePath);
  38. }
  39. return;
  40. }
  41. //添加,统一后缀名为pdf
  42. if (!parameterModel.savePath.pathExtension) {
  43. parameterModel.savePath = [parameterModel.savePath stringByAppendingString:@".pdf"];
  44. }else{
  45. NSString *lowString = [parameterModel.savePath.pathExtension lowercaseString];
  46. if (![lowString isEqualToString:@"pdf"]) {
  47. parameterModel.savePath = [parameterModel.savePath stringByAppendingString:@".pdf"];
  48. }else{
  49. NSMutableString *mNewName = [parameterModel.savePath mutableCopy];
  50. parameterModel.savePath = [mNewName stringByReplacingOccurrencesOfString:parameterModel.savePath.pathExtension withString:@"pdf"];
  51. }
  52. }
  53. //创建画布
  54. CGRect mediaBox;
  55. CGContextRef myPDFContext = NULL;
  56. mediaBox = CGRectMake(0, 0, parameterModel.pageSize.width, parameterModel.pageSize.height);
  57. CFStringRef ref = (__bridge CFStringRef)parameterModel.savePath;
  58. myPDFContext = MyPDFContextCreate (&mediaBox, ref);
  59. //如果画布为空,回调错误,返回
  60. if (!myPDFContext) {
  61. if (handle) {
  62. handle(NO,parameterModel.savePath);
  63. }
  64. return;
  65. }
  66. CFStringRef myKeys[1];
  67. CFTypeRef myValues[1];
  68. myKeys[0] = kCGPDFContextMediaBox;
  69. myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
  70. CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,
  71. (const void **) myValues, 1,
  72. &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
  73. //遍历将要打印的数组,打印PDFpage
  74. for (NSInteger j = 0 ;j <printArr.count;j++ )
  75. {
  76. //开始画PDF
  77. NSArray *subArr = printArr[j];
  78. CGPDFContextBeginPage(myPDFContext, pageDictionary);
  79. [self drawPageWithContext:myPDFContext parameterModel:parameterModel printArray:subArr];
  80. CGContextEndPage(myPDFContext);
  81. }
  82. CFRelease(pageDictionary);// 8
  83. CFRelease(myValues[0]);
  84. CGPDFContextClose(myPDFContext);
  85. CGContextRelease(myPDFContext);
  86. if (handle) {
  87. handle(YES,parameterModel.savePath);
  88. }
  89. }
  90. //获取最终用来绘制的数组
  91. - (NSArray *)fetchDrawPageArraysWithModel:(KMBookletParameterModel *)parameterModel
  92. {
  93. //获取page数组(双面打印时)
  94. NSMutableArray *printArr = [self regroupBothSidesPrintArrayWithPrimaryArray:parameterModel.pageArray];
  95. //如果是正面或者背面,那么对数组做处理
  96. if (parameterModel.printStyle == kBLPrintStyle_FrontOnly) {
  97. [printArr subArrayWithType:KMSubArrayTypeOdd];
  98. }else if (parameterModel.printStyle == kBLPrintStyle_BackOnly){
  99. [printArr subArrayWithType:KMSubArrayTypeEven];
  100. }
  101. //是否需要交换位置,当为左边,或者为左高的时候,需要交换两个Page的左右位置
  102. BOOL reverse = NO;
  103. switch (parameterModel.layoutStyle) {
  104. case kBLLayoutStyle_left:
  105. reverse = YES;
  106. break;
  107. case kBLLayoutStyle_leftTall:
  108. reverse = YES;
  109. break;
  110. default:
  111. break;
  112. }
  113. if (reverse) {
  114. for (NSMutableArray *arr in printArr) {
  115. PDFPage *page = [arr.firstObject copy];
  116. [arr removeObjectAtIndex:0];
  117. [arr addObject:page];
  118. }
  119. }
  120. //需要颠倒顺序如:@[@"1",@"2",@"3"] --> @[@"3",@"2",@"1"];
  121. if (parameterModel.reversePages) {
  122. //只有数组个数大于1才有换序的必要
  123. if (printArr.count > 1) {
  124. NSMutableArray *reverseArray = [NSMutableArray new];
  125. for (NSInteger i = printArr.count; i > 0 ; i--) {
  126. [reverseArray addObject:printArr[i -1]];
  127. }
  128. [printArr removeAllObjects];
  129. [printArr addObjectsFromArray:reverseArray];
  130. }
  131. }
  132. //计算最终的数组,页数
  133. NSMutableArray *newArray = [NSMutableArray new];
  134. //数组的起点和终点
  135. NSInteger firstPageIndexPlusOne = 0;
  136. NSInteger lastPageIndexPlusOne = 0;
  137. //如果是双面打印,那么就要乘以2
  138. if (parameterModel.printStyle == kBLPrintStyle_BothSide) {
  139. firstPageIndexPlusOne = 2 * parameterModel.sheetFromIndex;
  140. lastPageIndexPlusOne = 2 * parameterModel.sheetEndindex;
  141. }else{
  142. firstPageIndexPlusOne = parameterModel.sheetFromIndex;
  143. lastPageIndexPlusOne = parameterModel.sheetEndindex;
  144. }
  145. //提取数组元素
  146. for (NSInteger i = firstPageIndexPlusOne; i < lastPageIndexPlusOne ; i ++) {
  147. [newArray addObject:printArr[i]];
  148. }
  149. [printArr removeAllObjects];
  150. [printArr addObjectsFromArray:newArray];
  151. return printArr;
  152. }
  153. - (void)drawPageWithContext:(CGContextRef) myPDFContext
  154. parameterModel:(KMBookletParameterModel *)parameterModel
  155. printArray:(NSArray *)subArr
  156. {
  157. for (NSInteger i = 0; i < subArr.count; i++) {
  158. {
  159. //定义X偏移,Y偏移,缩放比例
  160. CGFloat xTransform = 0;
  161. CGFloat yTransform = 0;
  162. CGFloat scale = 0;
  163. //将用来绘制的PDFPage对象
  164. PDFPage *page = subArr[i];
  165. //是否需要移除注释
  166. NSMutableArray *annotations = [NSMutableArray array];
  167. for (PDFAnnotation *annotation in page.annotations) {
  168. [annotations addObject:annotation];
  169. }
  170. if (parameterModel.commentForms == kBookletCommentForms_DocumentOnly) {
  171. for (PDFAnnotation *annotation in annotations) {
  172. [annotation.page removeAnnotation:annotation];
  173. }
  174. } else if (parameterModel.commentForms == kBookletCommentForms_DocumentAndStamps) {
  175. for (PDFAnnotation *annotation in annotations) {
  176. if (![annotation isKindOfClass:[PDFAnnotationStamp class]]) {
  177. [annotation.page removeAnnotation:annotation];
  178. }
  179. }
  180. }
  181. //page原尺寸
  182. CGSize originalPageSize = [page boundsForBox:kPDFDisplayBoxCropBox].size;
  183. //自动旋转
  184. if (parameterModel.autoRotatePage) {
  185. //旋转规则:如果当前的实际宽高(由宽高和旋转角度得到)的关系是宽大于高,那么要逆时针旋转90度;
  186. if (page.rotation == 0) {
  187. CGFloat actualWidth = originalPageSize.width;
  188. CGFloat actualHeight = originalPageSize.height;
  189. if (actualWidth > actualHeight) {
  190. page.rotation = page.rotation - 90;
  191. }
  192. }else if (page.rotation == 90){
  193. CGFloat actualWidth = originalPageSize.height;
  194. CGFloat actualHeight = originalPageSize.width;
  195. if (actualWidth > actualHeight) {
  196. page.rotation = page.rotation -90;
  197. }
  198. }else if (page.rotation == 180){
  199. CGFloat actualWidth = originalPageSize.width;
  200. CGFloat actualHeight = originalPageSize.height;
  201. if (actualWidth > actualHeight) {
  202. page.rotation = page.rotation - 90;
  203. }
  204. }else if (page.rotation == 270){
  205. CGFloat actualWidth = originalPageSize.height;
  206. CGFloat actualHeight = originalPageSize.width;
  207. if (actualWidth > actualHeight) {
  208. page.rotation = page.rotation - 90;
  209. }
  210. }
  211. }
  212. //如果page的旋转角度对180求余还真,那么要将宽高大小交换
  213. if (page.rotation%180) {
  214. originalPageSize = CGSizeMake(originalPageSize.height, originalPageSize.width);
  215. }
  216. //画布大小
  217. CGSize availableSize = CGSizeMake((parameterModel.pageSize.width - parameterModel.gap)/2, parameterModel.pageSize.height);
  218. //画布和原来的page宽之比
  219. CGFloat wRatio = availableSize.width/originalPageSize.width;
  220. //画布和原来的page高之比
  221. CGFloat hRatio = availableSize.height/originalPageSize.height;
  222. //取最小值
  223. CGFloat ratio = MIN(wRatio, hRatio);
  224. //如果scale大于1那么等于1(只允许缩小,不可以放大)
  225. scale = ratio > 1 ? 1 : ratio;
  226. //在画布上page的实际尺寸
  227. CGSize realSize = CGSizeMake(originalPageSize.width * scale, originalPageSize.height *scale);
  228. //画第一张和画第二张x偏移区分
  229. if (i == 0) {
  230. xTransform = (availableSize.width - realSize.width)/2;
  231. }else{
  232. xTransform = (availableSize.width - realSize.width)/2 + parameterModel.pageSize.width/2 + parameterModel.gap/2;
  233. }
  234. //Y偏移
  235. yTransform = (availableSize.height - realSize.height)/2;
  236. //画布状态入栈
  237. CGContextSaveGState(myPDFContext);
  238. //偏移
  239. CGContextTranslateCTM(myPDFContext, xTransform, yTransform);
  240. //缩放
  241. CGContextScaleCTM(myPDFContext, scale, scale);
  242. //系统区分,绘制
  243. if (@available(macOS 10.12, *)) {
  244. [page drawWithBox:kPDFDisplayBoxCropBox toContext:myPDFContext];
  245. [page transformContext:myPDFContext forBox:kPDFDisplayBoxCropBox];
  246. } else {
  247. [NSGraphicsContext saveGraphicsState];
  248. [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:myPDFContext flipped:YES]];
  249. [page drawWithBox:kPDFDisplayBoxCropBox];
  250. [NSGraphicsContext restoreGraphicsState];
  251. [page transformContextForBox:kPDFDisplayBoxCropBox];
  252. }
  253. //画布状态出栈
  254. CGContextRestoreGState(myPDFContext);
  255. }
  256. }
  257. }
  258. //创建一个图形上下文对象
  259. CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
  260. CFStringRef path)
  261. {
  262. CGContextRef myOutContext = NULL;
  263. CFURLRef url;
  264. url = CFURLCreateWithFileSystemPath (NULL, // 1
  265. path,
  266. kCFURLPOSIXPathStyle,
  267. false);
  268. if (url != NULL) {
  269. myOutContext = CGPDFContextCreateWithURL (url,// 2
  270. inMediaBox,
  271. NULL);
  272. CFRelease(url);// 3
  273. }
  274. return myOutContext;// 4
  275. }
  276. #pragma mark - caculate
  277. - (NSMutableArray *)regroupBothSidesPrintArrayWithPrimaryArray:(NSArray *)primaryArray
  278. {
  279. NSMutableArray * backArray = [NSMutableArray array];
  280. NSDictionary *infoDict = [self calculatePagesPerBookletWithBookletNumber:1 totalPage:primaryArray.count];
  281. //小册子对应的页数信息
  282. NSDictionary *detailInfoDict = infoDict[BLBookletInfoKey];
  283. //空白页数量
  284. NSInteger blankPageCount = [(NSNumber *)infoDict[BLBlankPageCountKey] integerValue];
  285. NSMutableArray *tempArray = [NSMutableArray arrayWithArray:primaryArray];
  286. for (NSInteger i = 0; i < blankPageCount; i ++) {
  287. PDFPage *page = [[PDFPage alloc] init];
  288. [tempArray addObject:page];
  289. }
  290. NSInteger sheetCount = [detailInfoDict.allValues.lastObject integerValue];
  291. for (NSInteger i = 0; i < sheetCount; i ++) {
  292. NSMutableArray *tArr1 = [NSMutableArray new];
  293. PDFPage *addingPage1 = tempArray[tempArray.count - 2 * i - 1];
  294. PDFPage *addingPage2 = tempArray[2 * i];
  295. [tArr1 addObject:addingPage2];
  296. [tArr1 addObject:addingPage1];
  297. [backArray addObject:tArr1];
  298. NSMutableArray *tArr2 = [NSMutableArray new];
  299. PDFPage *addingPage3 = tempArray[2 * i + 1];
  300. PDFPage *addingPage4 = tempArray[tempArray.count - 2 * i -2];
  301. [tArr2 addObject:addingPage4];
  302. [tArr2 addObject:addingPage3];
  303. [backArray addObject:tArr2];
  304. }
  305. return backArray;
  306. }
  307. - (NSDictionary *)calculatePagesPerBookletWithBookletNumber:(NSInteger)bookletNum
  308. totalPage:(NSInteger)totalPageNum
  309. {
  310. NSMutableDictionary *backDict = [NSMutableDictionary dictionary];
  311. NSMutableDictionary *bookletPageNumInfoDict = [NSMutableDictionary dictionary];
  312. //对4取余,得到最后一个sheet会有多少张page
  313. NSInteger surplusPages = totalPageNum % 4;
  314. //总页数(每一页四页)= 总页数/4+是否有余数的bool值
  315. NSInteger tTotalPageNum = totalPageNum/4 + (surplusPages > 0 ? 1:0);
  316. //平均每一个册子有的页数
  317. NSInteger averagePageNum = tTotalPageNum / bookletNum;
  318. //平均分完还剩余多少页
  319. NSInteger surplusPageNum = tTotalPageNum % bookletNum;
  320. //遍历,给小册子赋值(数量)
  321. for (NSInteger i = 0; i < bookletNum; i++) {
  322. [bookletPageNumInfoDict setObject:[NSNumber numberWithInteger:averagePageNum] forKey:[NSString stringWithFormat:@"%ld",i]];
  323. }
  324. //遍历,给靠前的小册子添加页数
  325. for (NSInteger j = 0; j < surplusPageNum; j++) {
  326. [bookletPageNumInfoDict setObject:[NSNumber numberWithInteger:averagePageNum + 1] forKey:[NSString stringWithFormat:@"%ld",j]];
  327. }
  328. //空白页数量(分完了之后,最后一个小册子的空白页数量)
  329. NSInteger blankPageNum = tTotalPageNum * 4 - totalPageNum;
  330. [backDict setObject:[NSNumber numberWithInteger:blankPageNum] forKey:BLBlankPageCountKey];
  331. [backDict setObject:bookletPageNumInfoDict forKey:BLBookletInfoKey];
  332. return backDict;
  333. }
  334. @end