// // KMNBotaAnnotationHanddler.swift // PDF Reader Pro // // Created by User-Tangchao on 2024/12/6. // import Cocoa @objc enum KMNBotaAnnotationSortType: Int { case page = 0 case time = 1 } @objc protocol KMNBotaAnnotationHanddlerDelegate: NSObjectProtocol { @objc optional func dataDidInit(handdle: KMNBotaAnnotationHanddler, datas: [KMNBotaAnnotationSectionModel]?) } @objcMembers class KMNBotaAnnotationHanddler: NSObject { weak var pdfView: CPDFView? weak var delegate: KMNBotaAnnotationHanddlerDelegate? var pdfDocument: CPDFDocument? { get { return pdfView?.document } } // 按页面排序 var sortType: KMNBotaAnnotationSortType = .page { didSet { reloadAnnottionDatas() } } // 按升序排序 var sortSubType: KMSortMode = .ascending { didSet { reloadAnnottionDatas() } } var datas: [KMNBotaAnnotationSectionModel] = [] //实际展示的数据 var allOrgDatas: [KMNBotaAnnotationModel] = [] //所有显示的数据 func defaultallTypes() -> [String] { return [CPDFAnnotation.kType.highlight, CPDFAnnotation.kType.underline, CPDFAnnotation.kType.strikeout, CPDFAnnotation.kType.squiggly, CPDFAnnotation.kType.freehand, CPDFAnnotation.kType.freetext, CPDFAnnotation.kType.note, CPDFAnnotation.kType.square, CPDFAnnotation.kType.circle, CPDFAnnotation.kType.line, CPDFAnnotation.kType.arrow, CPDFAnnotation.kType.stamp, CPDFAnnotation.kType.image, CPDFAnnotation.kType.sign, CPDFAnnotation.kType.redact, CPDFAnnotation.kType.polyline, CPDFAnnotation.kType.polygon] } func reloadOrgAllAnnottionDatas(){ allOrgDatas = [] enumerateAllPages { page, stop in // 根据注释 Type 过滤 let filterNotes = getFilterNotes(types: defaultallTypes(), for: page) if filterNotes.isEmpty { return } for note in filterNotes { if note.annotationShouldDisplay() == false { // 注释已隐藏 continue } let model = KMNBotaAnnotationModel() model.annotation = note allOrgDatas.append(model) } } } func reloadAnnottionDatas(){ var showDatas:[KMNBotaAnnotationSectionModel] = [] if sortType == .page { var sortedOrgDatas:[KMNBotaAnnotationModel] = [] if sortSubType == .ascending { sortedOrgDatas = allOrgDatas.sorted { $0.annotation?.pageIndex() ?? 0 < $1.annotation?.pageIndex() ?? 0 } } else { sortedOrgDatas = allOrgDatas.sorted { $0.annotation?.pageIndex() ?? 0 > $1.annotation?.pageIndex() ?? 0 } } for annotationModel in sortedOrgDatas { var isNeedAdd = true for sectionModel in showDatas { if let item = sectionModel.items.first { if(item.annotation?.pageIndex() == annotationModel.annotation?.pageIndex()) { sectionModel.items.append(annotationModel) isNeedAdd = false break } } } if isNeedAdd == false { let sectionModel = KMNBotaAnnotationSectionModel() sectionModel.items.append(annotationModel) showDatas.append(sectionModel) } } } else if sortType == .time { var sortedOrgDatas:[KMNBotaAnnotationModel] = [] if sortSubType == .ascending { let sortedOrgDatas = allOrgDatas.sorted { $0.annotationDate.timeIntervalSince1970 < $1.annotationDate.timeIntervalSince1970 } } else { let sortedOrgDatas = allOrgDatas.sorted { $0.annotationDate.timeIntervalSince1970 > $1.annotationDate.timeIntervalSince1970 } } for annotationModel in sortedOrgDatas { var isNeedAdd = true for sectionModel in showDatas { if let item = sectionModel.items.first { let theDate = item.annotation?.modificationDate() let newDate = annotationModel.annotation?.modificationDate() if (theDate != nil) { if theDate!.isSameDay(other: newDate) { sectionModel.items.append(annotationModel) isNeedAdd = false break } } } } if isNeedAdd == true { let sectionModel = KMNBotaAnnotationSectionModel() sectionModel.items.append(annotationModel) showDatas.append(sectionModel) } } } datas = showDatas delegate?.dataDidInit?(handdle: self, datas: datas) } // 获取 Page 的 注释列表 【根据 Types】 func getFilterNotes(types: [String], for page: CPDFPage) -> [CPDFAnnotation] { let notes = page.annotations ?? [] let filterNotes = Self.filterAnnotation(annotations: notes, types: types) return filterNotes as? [CPDFAnnotation] ?? [] } // 遍历 所有 Section 试图模型 【二分法 遍历】 /* orderedAscending: 左操作数小于右操作数 orderedDescending: 左操作数大于右操作数 orderedSame: 左右操作数相等 */ func enumeratedAllSectionViewModel(callback: ((_ viewModel: KMNBotaAnnotationSectionModel?, _ stop: inout Bool, _ result: inout ComparisonResult)->Void)) { var lowerBound = 0 var upperBound = datas.count var stop = false var result: ComparisonResult = .orderedSame while lowerBound < upperBound { let midIndex = lowerBound + (upperBound - lowerBound) / 2 callback(datas[midIndex], &stop, &result) if stop || result == .orderedSame { // 找到 break } if result == .orderedAscending { // 左操作数小于右操作数 则在 下半区查找 lowerBound = midIndex + 1 } else { upperBound = midIndex } } return callback(nil, &stop, &result) } // 获取所有 Page func enumerateAllPages(callback: ((_ page: CPDFPage, _ stop: inout Bool)->Void)) { let count = pdfDocument?.pageCount ?? 0 for i in 0 ..< count { guard let page = pdfDocument?.page(at: i) else { continue } var stop = false callback(page, &stop) if stop { break } } } class func filterAnnotation(annotations: [Any], types: [Any]) -> [Any] { let array = annotations let leftExpression = NSExpression(forKeyPath: "type") let rightExpression = NSExpression(forConstantValue: types) let predicate = NSComparisonPredicate( leftExpression: leftExpression, rightExpression: rightExpression, modifier: .direct, type: .in, options: .diacriticInsensitive ) return (array as NSArray).filtered(using: predicate) } }