├── .gitignore ├── License.txt ├── PDFKitten.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ ├── PDFKitten.xccheckout │ └── WorkspaceSettings.xcsettings ├── PDFKitten ├── CIDFont.h ├── CIDFont.m ├── CIDType0Font.h ├── CIDType0Font.m ├── CIDType2Font.h ├── CIDType2Font.m ├── CMap.h ├── CMap.m ├── CompositeFont.h ├── CompositeFont.m ├── DocumentsView.h ├── DocumentsView.m ├── Font.h ├── Font.m ├── FontCollection.h ├── FontCollection.m ├── FontDescriptor.h ├── FontDescriptor.m ├── FontFile.h ├── FontFile.m ├── MMType1Font.h ├── MMType1Font.m ├── PDFKitten-Info.plist ├── PDFKitten-Prefix.pch ├── PDFKittenAppDelegate.h ├── PDFKittenAppDelegate.m ├── PDFPage.h ├── PDFPage.m ├── PDFPageDetailsView.h ├── PDFPageDetailsView.m ├── Page.h ├── Page.m ├── PageView.h ├── PageView.m ├── PageViewController.h ├── PageViewController.m ├── PageViewController.xib ├── RenderingState.h ├── RenderingState.m ├── RenderingStateStack.h ├── RenderingStateStack.m ├── RootViewController.h ├── RootViewController.m ├── Samples │ ├── Greek.pdf │ └── Kurt the Cat.pdf ├── Scanner.h ├── Scanner.m ├── Selection.h ├── Selection.m ├── SimpleFont.h ├── SimpleFont.m ├── StringDetector.h ├── StringDetector.m ├── StringDetectorDelegate.h ├── TrueTypeFont.h ├── TrueTypeFont.m ├── Type0Font.h ├── Type0Font.m ├── Type1Font.h ├── Type1Font.m ├── Type3Font.h ├── Type3Font.m ├── en.lproj │ ├── InfoPlist.strings │ └── MainWindow.xib ├── main.m └── pdfScannerCallbacks.mm ├── PDFKittenTests ├── KurtStory.txt ├── PDFKittenTests-Info.plist ├── PDFKittenTests-Prefix.pch ├── StringDetectorTest.h ├── StringDetectorTest.m └── en.lproj │ └── InfoPlist.strings ├── README.md └── docs ├── .gitignore └── PDFKitten-DeveloperManual.tex /.gitignore: -------------------------------------------------------------------------------- 1 | PDFKitten.xcodeproj/project.xcworkspace/xcuserdata/ 2 | PDFKitten.xcodeproj/xcuserdata/ 3 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 Marcus Hedenström 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PDFKitten.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2C06404314C782DF00A9B33F /* FontFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C06404214C782DF00A9B33F /* FontFile.m */; }; 11 | 2C0E15AC13589B0B004096C7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E15AB13589B0B004096C7 /* UIKit.framework */; }; 12 | 2C0E15AE13589B0B004096C7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E15AD13589B0B004096C7 /* Foundation.framework */; }; 13 | 2C0E15B013589B0B004096C7 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E15AF13589B0B004096C7 /* CoreGraphics.framework */; }; 14 | 2C1BB5CE168B97960026F266 /* KurtStory.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2C1BB5CB168B91F50026F266 /* KurtStory.txt */; }; 15 | 2C209A9215C2B36900D4437B /* CIDFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C209A9115C2B36900D4437B /* CIDFont.m */; }; 16 | 2C265927154D9C3900E11745 /* RenderingStateStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C265926154D9C3900E11745 /* RenderingStateStack.m */; }; 17 | 2C35AD70167E02E000BA5F79 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E15AB13589B0B004096C7 /* UIKit.framework */; }; 18 | 2C35AD71167E02E000BA5F79 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C0E15AD13589B0B004096C7 /* Foundation.framework */; }; 19 | 2C35AD77167E02E000BA5F79 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2C35AD75167E02E000BA5F79 /* InfoPlist.strings */; }; 20 | 2C35AD83167E055A00BA5F79 /* StringDetectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C35AD82167E055A00BA5F79 /* StringDetectorTest.m */; }; 21 | 2C38D8E713EF370700718B0D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C38D8E613EF370700718B0D /* Security.framework */; }; 22 | 2C47505F13F3E48200BC2DB9 /* DocumentsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C47505D13F3E48200BC2DB9 /* DocumentsView.m */; }; 23 | 2C77F7B41442381300767E6A /* Kurt the Cat.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 2C77F7B31442381300767E6A /* Kurt the Cat.pdf */; }; 24 | 2C855590137B336C00D58482 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C85558F137B336C00D58482 /* QuartzCore.framework */; }; 25 | 2CA0FC9413EEB11B0028DCCD /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA0FC9313EEB11B0028DCCD /* RootViewController.m */; }; 26 | 2CA0FC9713EEB1210028DCCD /* PDFPage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA0FC9613EEB1210028DCCD /* PDFPage.m */; }; 27 | 2CA0FCA113EEB1B80028DCCD /* Page.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA0FCA013EEB1B80028DCCD /* Page.m */; }; 28 | 2CA0FCA513EEB1C00028DCCD /* PageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA0FCA313EEB1C00028DCCD /* PageViewController.m */; }; 29 | 2CA0FCA613EEB1C00028DCCD /* PageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CA0FCA413EEB1C00028DCCD /* PageViewController.xib */; }; 30 | 2CB5B24D168CF50E009103E6 /* pdfScannerCallbacks.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2CB5B24B168CF50E009103E6 /* pdfScannerCallbacks.mm */; }; 31 | 2CC77EC114560E2E000E9B8E /* PDFPageDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CC77EC014560E2E000E9B8E /* PDFPageDetailsView.m */; }; 32 | 2CDC3F65138972E5006CC6FB /* StringDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CDC3F64138972E5006CC6FB /* StringDetector.m */; }; 33 | 2CDC3F80138977C1006CC6FB /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2CDC3F7A138977C0006CC6FB /* InfoPlist.strings */; }; 34 | 2CDC3F81138977C1006CC6FB /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CDC3F7C138977C0006CC6FB /* MainWindow.xib */; }; 35 | 2CE7BC9D13896EF0009784B9 /* PageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BC9B13896EF0009784B9 /* PageView.m */; }; 36 | 2CE7BCAF13896EFB009784B9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCA013896EFB009784B9 /* main.m */; }; 37 | 2CE7BCB113896EFB009784B9 /* PDFKittenAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCA413896EFB009784B9 /* PDFKittenAppDelegate.m */; }; 38 | 2CE7BCC413897135009784B9 /* CIDType0Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCBD13897135009784B9 /* CIDType0Font.m */; }; 39 | 2CE7BCC513897135009784B9 /* CIDType2Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCBF13897135009784B9 /* CIDType2Font.m */; }; 40 | 2CE7BCC613897135009784B9 /* CMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCC113897135009784B9 /* CMap.m */; }; 41 | 2CE7BCC713897135009784B9 /* CompositeFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCC313897135009784B9 /* CompositeFont.m */; }; 42 | 2CE7BCCE1389713D009784B9 /* Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCC91389713D009784B9 /* Font.m */; }; 43 | 2CE7BCCF1389713D009784B9 /* FontCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCCB1389713D009784B9 /* FontCollection.m */; }; 44 | 2CE7BCD01389713D009784B9 /* FontDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCCD1389713D009784B9 /* FontDescriptor.m */; }; 45 | 2CE7BCD313897142009784B9 /* MMType1Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCD213897142009784B9 /* MMType1Font.m */; }; 46 | 2CE7BCD61389714F009784B9 /* SimpleFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCD51389714F009784B9 /* SimpleFont.m */; }; 47 | 2CE7BCDF13897159009784B9 /* TrueTypeFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCD813897159009784B9 /* TrueTypeFont.m */; }; 48 | 2CE7BCE013897159009784B9 /* Type0Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCDA13897159009784B9 /* Type0Font.m */; }; 49 | 2CE7BCE113897159009784B9 /* Type1Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCDC13897159009784B9 /* Type1Font.m */; }; 50 | 2CE7BCE213897159009784B9 /* Type3Font.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCDE13897159009784B9 /* Type3Font.m */; }; 51 | 2CE7BCE713897245009784B9 /* Scanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCE613897245009784B9 /* Scanner.m */; }; 52 | 2CE7BCEB1389725D009784B9 /* RenderingState.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCEA1389725D009784B9 /* RenderingState.m */; }; 53 | 2CE7BCEF13897294009784B9 /* Selection.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CE7BCEE13897294009784B9 /* Selection.m */; }; 54 | /* End PBXBuildFile section */ 55 | 56 | /* Begin PBXContainerItemProxy section */ 57 | 2C35AD7F167E035B00BA5F79 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 2C0E159E13589B0B004096C7 /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = 2C0E15A613589B0B004096C7; 62 | remoteInfo = PDFKitten; 63 | }; 64 | /* End PBXContainerItemProxy section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 2C06404114C782DE00A9B33F /* FontFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontFile.h; path = PDFKitten/FontFile.h; sourceTree = SOURCE_ROOT; }; 68 | 2C06404214C782DF00A9B33F /* FontFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FontFile.m; path = PDFKitten/FontFile.m; sourceTree = SOURCE_ROOT; }; 69 | 2C0E15AB13589B0B004096C7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 70 | 2C0E15AD13589B0B004096C7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 71 | 2C0E15AF13589B0B004096C7 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 72 | 2C1BB5CB168B91F50026F266 /* KurtStory.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = KurtStory.txt; sourceTree = ""; }; 73 | 2C209A9015C2B36900D4437B /* CIDFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CIDFont.h; path = PDFKitten/CIDFont.h; sourceTree = SOURCE_ROOT; }; 74 | 2C209A9115C2B36900D4437B /* CIDFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CIDFont.m; path = PDFKitten/CIDFont.m; sourceTree = SOURCE_ROOT; }; 75 | 2C265924154D9C0E00E11745 /* RenderingStateStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RenderingStateStack.h; path = PDFKitten/RenderingStateStack.h; sourceTree = SOURCE_ROOT; }; 76 | 2C265926154D9C3900E11745 /* RenderingStateStack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RenderingStateStack.m; path = PDFKitten/RenderingStateStack.m; sourceTree = SOURCE_ROOT; }; 77 | 2C35AD6E167E02E000BA5F79 /* PDFKittenTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PDFKittenTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | 2C35AD74167E02E000BA5F79 /* PDFKittenTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PDFKittenTests-Info.plist"; sourceTree = ""; }; 79 | 2C35AD76167E02E000BA5F79 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 80 | 2C35AD7B167E02E000BA5F79 /* PDFKittenTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PDFKittenTests-Prefix.pch"; sourceTree = ""; }; 81 | 2C35AD81167E055A00BA5F79 /* StringDetectorTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringDetectorTest.h; sourceTree = ""; }; 82 | 2C35AD82167E055A00BA5F79 /* StringDetectorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StringDetectorTest.m; sourceTree = ""; }; 83 | 2C38D8E613EF370700718B0D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 84 | 2C47505C13F3E48200BC2DB9 /* DocumentsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DocumentsView.h; path = PDFKitten/DocumentsView.h; sourceTree = SOURCE_ROOT; }; 85 | 2C47505D13F3E48200BC2DB9 /* DocumentsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DocumentsView.m; path = PDFKitten/DocumentsView.m; sourceTree = SOURCE_ROOT; }; 86 | 2C77F7B31442381300767E6A /* Kurt the Cat.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "Kurt the Cat.pdf"; path = "PDFKitten/Samples/Kurt the Cat.pdf"; sourceTree = SOURCE_ROOT; }; 87 | 2C81CDF5168C79C200CB98EF /* StringDetectorDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringDetectorDelegate.h; path = PDFKitten/StringDetectorDelegate.h; sourceTree = SOURCE_ROOT; }; 88 | 2C85558F137B336C00D58482 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 89 | 2CA0FC9213EEB11B0028DCCD /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RootViewController.h; path = PDFKitten/RootViewController.h; sourceTree = SOURCE_ROOT; }; 90 | 2CA0FC9313EEB11B0028DCCD /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RootViewController.m; path = PDFKitten/RootViewController.m; sourceTree = SOURCE_ROOT; }; 91 | 2CA0FC9513EEB1210028DCCD /* PDFPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PDFPage.h; path = PDFKitten/PDFPage.h; sourceTree = SOURCE_ROOT; }; 92 | 2CA0FC9613EEB1210028DCCD /* PDFPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PDFPage.m; path = PDFKitten/PDFPage.m; sourceTree = SOURCE_ROOT; }; 93 | 2CA0FC9F13EEB1B80028DCCD /* Page.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Page.h; path = PDFKitten/Page.h; sourceTree = SOURCE_ROOT; }; 94 | 2CA0FCA013EEB1B80028DCCD /* Page.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Page.m; path = PDFKitten/Page.m; sourceTree = SOURCE_ROOT; }; 95 | 2CA0FCA213EEB1BF0028DCCD /* PageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PageViewController.h; path = PDFKitten/PageViewController.h; sourceTree = SOURCE_ROOT; }; 96 | 2CA0FCA313EEB1C00028DCCD /* PageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PageViewController.m; path = PDFKitten/PageViewController.m; sourceTree = SOURCE_ROOT; }; 97 | 2CA0FCA413EEB1C00028DCCD /* PageViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PageViewController.xib; path = PDFKitten/PageViewController.xib; sourceTree = SOURCE_ROOT; }; 98 | 2CB5B24B168CF50E009103E6 /* pdfScannerCallbacks.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = pdfScannerCallbacks.mm; path = PDFKitten/pdfScannerCallbacks.mm; sourceTree = SOURCE_ROOT; }; 99 | 2CC77EBF14560E2D000E9B8E /* PDFPageDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PDFPageDetailsView.h; path = PDFKitten/PDFPageDetailsView.h; sourceTree = SOURCE_ROOT; }; 100 | 2CC77EC014560E2E000E9B8E /* PDFPageDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PDFPageDetailsView.m; path = PDFKitten/PDFPageDetailsView.m; sourceTree = SOURCE_ROOT; }; 101 | 2CDC3F63138972E5006CC6FB /* StringDetector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringDetector.h; path = PDFKitten/StringDetector.h; sourceTree = SOURCE_ROOT; }; 102 | 2CDC3F64138972E5006CC6FB /* StringDetector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StringDetector.m; path = PDFKitten/StringDetector.m; sourceTree = SOURCE_ROOT; }; 103 | 2CDC3F7B138977C0006CC6FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = PDFKitten/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 104 | 2CDC3F7D138977C1006CC6FB /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = PDFKitten/en.lproj/MainWindow.xib; sourceTree = SOURCE_ROOT; }; 105 | 2CE7BC491389674B009784B9 /* PDFKitten.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PDFKitten.app; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | 2CE7BC9A13896EF0009784B9 /* PageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PageView.h; path = PDFKitten/PageView.h; sourceTree = SOURCE_ROOT; }; 107 | 2CE7BC9B13896EF0009784B9 /* PageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PageView.m; path = PDFKitten/PageView.m; sourceTree = SOURCE_ROOT; }; 108 | 2CE7BC9F13896EFB009784B9 /* PDFKitten-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "PDFKitten-Prefix.pch"; path = "PDFKitten/PDFKitten-Prefix.pch"; sourceTree = SOURCE_ROOT; }; 109 | 2CE7BCA013896EFB009784B9 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = PDFKitten/main.m; sourceTree = SOURCE_ROOT; }; 110 | 2CE7BCA313896EFB009784B9 /* PDFKittenAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PDFKittenAppDelegate.h; path = PDFKitten/PDFKittenAppDelegate.h; sourceTree = SOURCE_ROOT; }; 111 | 2CE7BCA413896EFB009784B9 /* PDFKittenAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PDFKittenAppDelegate.m; path = PDFKitten/PDFKittenAppDelegate.m; sourceTree = SOURCE_ROOT; }; 112 | 2CE7BCAA13896EFB009784B9 /* PDFKitten-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "PDFKitten-Info.plist"; path = "PDFKitten/PDFKitten-Info.plist"; sourceTree = SOURCE_ROOT; }; 113 | 2CE7BCBC13897135009784B9 /* CIDType0Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CIDType0Font.h; path = PDFKitten/CIDType0Font.h; sourceTree = SOURCE_ROOT; }; 114 | 2CE7BCBD13897135009784B9 /* CIDType0Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CIDType0Font.m; path = PDFKitten/CIDType0Font.m; sourceTree = SOURCE_ROOT; }; 115 | 2CE7BCBE13897135009784B9 /* CIDType2Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CIDType2Font.h; path = PDFKitten/CIDType2Font.h; sourceTree = SOURCE_ROOT; }; 116 | 2CE7BCBF13897135009784B9 /* CIDType2Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CIDType2Font.m; path = PDFKitten/CIDType2Font.m; sourceTree = SOURCE_ROOT; }; 117 | 2CE7BCC013897135009784B9 /* CMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CMap.h; path = PDFKitten/CMap.h; sourceTree = SOURCE_ROOT; }; 118 | 2CE7BCC113897135009784B9 /* CMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CMap.m; path = PDFKitten/CMap.m; sourceTree = SOURCE_ROOT; }; 119 | 2CE7BCC213897135009784B9 /* CompositeFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompositeFont.h; path = PDFKitten/CompositeFont.h; sourceTree = SOURCE_ROOT; }; 120 | 2CE7BCC313897135009784B9 /* CompositeFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CompositeFont.m; path = PDFKitten/CompositeFont.m; sourceTree = SOURCE_ROOT; }; 121 | 2CE7BCC81389713D009784B9 /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Font.h; path = PDFKitten/Font.h; sourceTree = SOURCE_ROOT; }; 122 | 2CE7BCC91389713D009784B9 /* Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Font.m; path = PDFKitten/Font.m; sourceTree = SOURCE_ROOT; }; 123 | 2CE7BCCA1389713D009784B9 /* FontCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontCollection.h; path = PDFKitten/FontCollection.h; sourceTree = SOURCE_ROOT; }; 124 | 2CE7BCCB1389713D009784B9 /* FontCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FontCollection.m; path = PDFKitten/FontCollection.m; sourceTree = SOURCE_ROOT; }; 125 | 2CE7BCCC1389713D009784B9 /* FontDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FontDescriptor.h; path = PDFKitten/FontDescriptor.h; sourceTree = SOURCE_ROOT; }; 126 | 2CE7BCCD1389713D009784B9 /* FontDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FontDescriptor.m; path = PDFKitten/FontDescriptor.m; sourceTree = SOURCE_ROOT; }; 127 | 2CE7BCD113897142009784B9 /* MMType1Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMType1Font.h; path = PDFKitten/MMType1Font.h; sourceTree = SOURCE_ROOT; }; 128 | 2CE7BCD213897142009784B9 /* MMType1Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMType1Font.m; path = PDFKitten/MMType1Font.m; sourceTree = SOURCE_ROOT; }; 129 | 2CE7BCD41389714F009784B9 /* SimpleFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleFont.h; path = PDFKitten/SimpleFont.h; sourceTree = SOURCE_ROOT; }; 130 | 2CE7BCD51389714F009784B9 /* SimpleFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SimpleFont.m; path = PDFKitten/SimpleFont.m; sourceTree = SOURCE_ROOT; }; 131 | 2CE7BCD713897159009784B9 /* TrueTypeFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TrueTypeFont.h; path = PDFKitten/TrueTypeFont.h; sourceTree = SOURCE_ROOT; }; 132 | 2CE7BCD813897159009784B9 /* TrueTypeFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TrueTypeFont.m; path = PDFKitten/TrueTypeFont.m; sourceTree = SOURCE_ROOT; }; 133 | 2CE7BCD913897159009784B9 /* Type0Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Type0Font.h; path = PDFKitten/Type0Font.h; sourceTree = SOURCE_ROOT; }; 134 | 2CE7BCDA13897159009784B9 /* Type0Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Type0Font.m; path = PDFKitten/Type0Font.m; sourceTree = SOURCE_ROOT; }; 135 | 2CE7BCDB13897159009784B9 /* Type1Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Type1Font.h; path = PDFKitten/Type1Font.h; sourceTree = SOURCE_ROOT; }; 136 | 2CE7BCDC13897159009784B9 /* Type1Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Type1Font.m; path = PDFKitten/Type1Font.m; sourceTree = SOURCE_ROOT; }; 137 | 2CE7BCDD13897159009784B9 /* Type3Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Type3Font.h; path = PDFKitten/Type3Font.h; sourceTree = SOURCE_ROOT; }; 138 | 2CE7BCDE13897159009784B9 /* Type3Font.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Type3Font.m; path = PDFKitten/Type3Font.m; sourceTree = SOURCE_ROOT; }; 139 | 2CE7BCE513897245009784B9 /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Scanner.h; path = PDFKitten/Scanner.h; sourceTree = SOURCE_ROOT; }; 140 | 2CE7BCE613897245009784B9 /* Scanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Scanner.m; path = PDFKitten/Scanner.m; sourceTree = SOURCE_ROOT; }; 141 | 2CE7BCE91389725D009784B9 /* RenderingState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RenderingState.h; path = PDFKitten/RenderingState.h; sourceTree = SOURCE_ROOT; }; 142 | 2CE7BCEA1389725D009784B9 /* RenderingState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RenderingState.m; path = PDFKitten/RenderingState.m; sourceTree = SOURCE_ROOT; }; 143 | 2CE7BCED13897293009784B9 /* Selection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Selection.h; path = PDFKitten/Selection.h; sourceTree = SOURCE_ROOT; }; 144 | 2CE7BCEE13897294009784B9 /* Selection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Selection.m; path = PDFKitten/Selection.m; sourceTree = SOURCE_ROOT; }; 145 | /* End PBXFileReference section */ 146 | 147 | /* Begin PBXFrameworksBuildPhase section */ 148 | 2C0E15A413589B0B004096C7 /* Frameworks */ = { 149 | isa = PBXFrameworksBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | 2C0E15AC13589B0B004096C7 /* UIKit.framework in Frameworks */, 153 | 2C0E15AE13589B0B004096C7 /* Foundation.framework in Frameworks */, 154 | 2C0E15B013589B0B004096C7 /* CoreGraphics.framework in Frameworks */, 155 | 2C855590137B336C00D58482 /* QuartzCore.framework in Frameworks */, 156 | 2C38D8E713EF370700718B0D /* Security.framework in Frameworks */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | 2C35AD6A167E02E000BA5F79 /* Frameworks */ = { 161 | isa = PBXFrameworksBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 2C35AD70167E02E000BA5F79 /* UIKit.framework in Frameworks */, 165 | 2C35AD71167E02E000BA5F79 /* Foundation.framework in Frameworks */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXFrameworksBuildPhase section */ 170 | 171 | /* Begin PBXGroup section */ 172 | 2C0E159C13589B0B004096C7 = { 173 | isa = PBXGroup; 174 | children = ( 175 | 2C0E15B113589B0B004096C7 /* PDFKitten */, 176 | 2C35AD72167E02E000BA5F79 /* PDFKittenTests */, 177 | 2C0E15AA13589B0B004096C7 /* Frameworks */, 178 | 2C0E15A813589B0B004096C7 /* Products */, 179 | ); 180 | sourceTree = ""; 181 | }; 182 | 2C0E15A813589B0B004096C7 /* Products */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 2CE7BC491389674B009784B9 /* PDFKitten.app */, 186 | 2C35AD6E167E02E000BA5F79 /* PDFKittenTests.octest */, 187 | ); 188 | name = Products; 189 | sourceTree = ""; 190 | }; 191 | 2C0E15AA13589B0B004096C7 /* Frameworks */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 2C38D8E613EF370700718B0D /* Security.framework */, 195 | 2C85558F137B336C00D58482 /* QuartzCore.framework */, 196 | 2C0E15AB13589B0B004096C7 /* UIKit.framework */, 197 | 2C0E15AD13589B0B004096C7 /* Foundation.framework */, 198 | 2C0E15AF13589B0B004096C7 /* CoreGraphics.framework */, 199 | ); 200 | name = Frameworks; 201 | sourceTree = ""; 202 | }; 203 | 2C0E15B113589B0B004096C7 /* PDFKitten */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 2CA0FCA713EEB32D0028DCCD /* PDF Core */, 207 | 2C0E15B213589B0B004096C7 /* View Controllers */, 208 | 2CA0FC9A13EEB1430028DCCD /* Resources */, 209 | 2CA0FC9E13EEB1910028DCCD /* Other Sources */, 210 | ); 211 | name = PDFKitten; 212 | path = PDFDemo; 213 | sourceTree = ""; 214 | }; 215 | 2C0E15B213589B0B004096C7 /* View Controllers */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | 2C12935213E585F00090C70F /* PageViewController */, 219 | 2CA0FC9213EEB11B0028DCCD /* RootViewController.h */, 220 | 2CA0FC9313EEB11B0028DCCD /* RootViewController.m */, 221 | 2CC77EBF14560E2D000E9B8E /* PDFPageDetailsView.h */, 222 | 2CC77EC014560E2E000E9B8E /* PDFPageDetailsView.m */, 223 | 2C47505C13F3E48200BC2DB9 /* DocumentsView.h */, 224 | 2C47505D13F3E48200BC2DB9 /* DocumentsView.m */, 225 | 2CE7BCA313896EFB009784B9 /* PDFKittenAppDelegate.h */, 226 | 2CE7BCA413896EFB009784B9 /* PDFKittenAppDelegate.m */, 227 | ); 228 | name = "View Controllers"; 229 | sourceTree = ""; 230 | }; 231 | 2C12935213E585F00090C70F /* PageViewController */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 2CA0FC9513EEB1210028DCCD /* PDFPage.h */, 235 | 2CA0FC9613EEB1210028DCCD /* PDFPage.m */, 236 | 2CA0FC9F13EEB1B80028DCCD /* Page.h */, 237 | 2CA0FCA013EEB1B80028DCCD /* Page.m */, 238 | 2CE7BC9A13896EF0009784B9 /* PageView.h */, 239 | 2CE7BC9B13896EF0009784B9 /* PageView.m */, 240 | 2CA0FCA213EEB1BF0028DCCD /* PageViewController.h */, 241 | 2CA0FCA313EEB1C00028DCCD /* PageViewController.m */, 242 | 2CA0FCA413EEB1C00028DCCD /* PageViewController.xib */, 243 | ); 244 | name = PageViewController; 245 | sourceTree = ""; 246 | }; 247 | 2C35AD72167E02E000BA5F79 /* PDFKittenTests */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | 2C35AD81167E055A00BA5F79 /* StringDetectorTest.h */, 251 | 2C35AD82167E055A00BA5F79 /* StringDetectorTest.m */, 252 | 2C35AD73167E02E000BA5F79 /* Supporting Files */, 253 | ); 254 | path = PDFKittenTests; 255 | sourceTree = ""; 256 | }; 257 | 2C35AD73167E02E000BA5F79 /* Supporting Files */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | 2C1BB5CB168B91F50026F266 /* KurtStory.txt */, 261 | 2C35AD74167E02E000BA5F79 /* PDFKittenTests-Info.plist */, 262 | 2C35AD75167E02E000BA5F79 /* InfoPlist.strings */, 263 | 2C35AD7B167E02E000BA5F79 /* PDFKittenTests-Prefix.pch */, 264 | ); 265 | name = "Supporting Files"; 266 | sourceTree = ""; 267 | }; 268 | 2CA0FC9A13EEB1430028DCCD /* Resources */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | 2C77F7B31442381300767E6A /* Kurt the Cat.pdf */, 272 | 2CDC3F7A138977C0006CC6FB /* InfoPlist.strings */, 273 | 2CE7BCAA13896EFB009784B9 /* PDFKitten-Info.plist */, 274 | 2CDC3F7C138977C0006CC6FB /* MainWindow.xib */, 275 | ); 276 | name = Resources; 277 | sourceTree = ""; 278 | }; 279 | 2CA0FC9E13EEB1910028DCCD /* Other Sources */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | 2CE7BC9F13896EFB009784B9 /* PDFKitten-Prefix.pch */, 283 | 2CE7BCA013896EFB009784B9 /* main.m */, 284 | ); 285 | name = "Other Sources"; 286 | sourceTree = ""; 287 | }; 288 | 2CA0FCA713EEB32D0028DCCD /* PDF Core */ = { 289 | isa = PBXGroup; 290 | children = ( 291 | 2CE7BCE413897225009784B9 /* Scanner */, 292 | 2CE7BCBB13897129009784B9 /* Font */, 293 | 2CE7BCE81389724B009784B9 /* RenderingState */, 294 | 2CDC3F61138972D4006CC6FB /* StringDetector */, 295 | 2CE7BCEC13897264009784B9 /* Selection */, 296 | ); 297 | name = "PDF Core"; 298 | sourceTree = ""; 299 | }; 300 | 2CDC3F61138972D4006CC6FB /* StringDetector */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | 2CDC3F63138972E5006CC6FB /* StringDetector.h */, 304 | 2CDC3F64138972E5006CC6FB /* StringDetector.m */, 305 | 2C81CDF5168C79C200CB98EF /* StringDetectorDelegate.h */, 306 | ); 307 | name = StringDetector; 308 | sourceTree = ""; 309 | }; 310 | 2CDC3F6613897343006CC6FB /* Simple Fonts */ = { 311 | isa = PBXGroup; 312 | children = ( 313 | 2CE7BCD41389714F009784B9 /* SimpleFont.h */, 314 | 2CE7BCD51389714F009784B9 /* SimpleFont.m */, 315 | 2CE7BCD713897159009784B9 /* TrueTypeFont.h */, 316 | 2CE7BCD813897159009784B9 /* TrueTypeFont.m */, 317 | 2CE7BCDB13897159009784B9 /* Type1Font.h */, 318 | 2CE7BCDC13897159009784B9 /* Type1Font.m */, 319 | 2CE7BCD113897142009784B9 /* MMType1Font.h */, 320 | 2CE7BCD213897142009784B9 /* MMType1Font.m */, 321 | 2CE7BCDD13897159009784B9 /* Type3Font.h */, 322 | 2CE7BCDE13897159009784B9 /* Type3Font.m */, 323 | ); 324 | name = "Simple Fonts"; 325 | sourceTree = ""; 326 | }; 327 | 2CDC3F6713897369006CC6FB /* Composite Fonts */ = { 328 | isa = PBXGroup; 329 | children = ( 330 | 2CE7BCC213897135009784B9 /* CompositeFont.h */, 331 | 2CE7BCC313897135009784B9 /* CompositeFont.m */, 332 | 2CE7BCD913897159009784B9 /* Type0Font.h */, 333 | 2CE7BCDA13897159009784B9 /* Type0Font.m */, 334 | 2C209A9015C2B36900D4437B /* CIDFont.h */, 335 | 2C209A9115C2B36900D4437B /* CIDFont.m */, 336 | 2CE7BCBC13897135009784B9 /* CIDType0Font.h */, 337 | 2CE7BCBD13897135009784B9 /* CIDType0Font.m */, 338 | 2CE7BCBE13897135009784B9 /* CIDType2Font.h */, 339 | 2CE7BCBF13897135009784B9 /* CIDType2Font.m */, 340 | ); 341 | name = "Composite Fonts"; 342 | sourceTree = ""; 343 | }; 344 | 2CE7BCBB13897129009784B9 /* Font */ = { 345 | isa = PBXGroup; 346 | children = ( 347 | 2CE7BCC013897135009784B9 /* CMap.h */, 348 | 2CE7BCC113897135009784B9 /* CMap.m */, 349 | 2CE7BCC81389713D009784B9 /* Font.h */, 350 | 2CE7BCC91389713D009784B9 /* Font.m */, 351 | 2CE7BCCA1389713D009784B9 /* FontCollection.h */, 352 | 2CE7BCCB1389713D009784B9 /* FontCollection.m */, 353 | 2CE7BCCC1389713D009784B9 /* FontDescriptor.h */, 354 | 2CE7BCCD1389713D009784B9 /* FontDescriptor.m */, 355 | 2CE7BCE31389715F009784B9 /* Subtypes */, 356 | 2C06404114C782DE00A9B33F /* FontFile.h */, 357 | 2C06404214C782DF00A9B33F /* FontFile.m */, 358 | ); 359 | name = Font; 360 | sourceTree = ""; 361 | }; 362 | 2CE7BCE31389715F009784B9 /* Subtypes */ = { 363 | isa = PBXGroup; 364 | children = ( 365 | 2CDC3F6713897369006CC6FB /* Composite Fonts */, 366 | 2CDC3F6613897343006CC6FB /* Simple Fonts */, 367 | ); 368 | name = Subtypes; 369 | sourceTree = ""; 370 | }; 371 | 2CE7BCE413897225009784B9 /* Scanner */ = { 372 | isa = PBXGroup; 373 | children = ( 374 | 2CB5B24B168CF50E009103E6 /* pdfScannerCallbacks.mm */, 375 | 2CE7BCE513897245009784B9 /* Scanner.h */, 376 | 2CE7BCE613897245009784B9 /* Scanner.m */, 377 | ); 378 | name = Scanner; 379 | sourceTree = ""; 380 | }; 381 | 2CE7BCE81389724B009784B9 /* RenderingState */ = { 382 | isa = PBXGroup; 383 | children = ( 384 | 2CE7BCE91389725D009784B9 /* RenderingState.h */, 385 | 2CE7BCEA1389725D009784B9 /* RenderingState.m */, 386 | 2C265924154D9C0E00E11745 /* RenderingStateStack.h */, 387 | 2C265926154D9C3900E11745 /* RenderingStateStack.m */, 388 | ); 389 | name = RenderingState; 390 | sourceTree = ""; 391 | }; 392 | 2CE7BCEC13897264009784B9 /* Selection */ = { 393 | isa = PBXGroup; 394 | children = ( 395 | 2CE7BCED13897293009784B9 /* Selection.h */, 396 | 2CE7BCEE13897294009784B9 /* Selection.m */, 397 | ); 398 | name = Selection; 399 | sourceTree = ""; 400 | }; 401 | /* End PBXGroup section */ 402 | 403 | /* Begin PBXNativeTarget section */ 404 | 2C0E15A613589B0B004096C7 /* PDFKitten */ = { 405 | isa = PBXNativeTarget; 406 | buildConfigurationList = 2C0E15DE13589B0C004096C7 /* Build configuration list for PBXNativeTarget "PDFKitten" */; 407 | buildPhases = ( 408 | 2C0E15A313589B0B004096C7 /* Sources */, 409 | 2C0E15A413589B0B004096C7 /* Frameworks */, 410 | 2C0E15A513589B0B004096C7 /* Resources */, 411 | ); 412 | buildRules = ( 413 | ); 414 | dependencies = ( 415 | ); 416 | name = PDFKitten; 417 | productName = PDFDemo; 418 | productReference = 2CE7BC491389674B009784B9 /* PDFKitten.app */; 419 | productType = "com.apple.product-type.application"; 420 | }; 421 | 2C35AD6D167E02E000BA5F79 /* PDFKittenTests */ = { 422 | isa = PBXNativeTarget; 423 | buildConfigurationList = 2C35AD7C167E02E000BA5F79 /* Build configuration list for PBXNativeTarget "PDFKittenTests" */; 424 | buildPhases = ( 425 | 2C35AD69167E02E000BA5F79 /* Sources */, 426 | 2C35AD6A167E02E000BA5F79 /* Frameworks */, 427 | 2C35AD6B167E02E000BA5F79 /* Resources */, 428 | 2C35AD6C167E02E000BA5F79 /* ShellScript */, 429 | ); 430 | buildRules = ( 431 | ); 432 | dependencies = ( 433 | 2C35AD80167E035B00BA5F79 /* PBXTargetDependency */, 434 | ); 435 | name = PDFKittenTests; 436 | productName = PDFKittenTests; 437 | productReference = 2C35AD6E167E02E000BA5F79 /* PDFKittenTests.octest */; 438 | productType = "com.apple.product-type.bundle.unit-test"; 439 | }; 440 | /* End PBXNativeTarget section */ 441 | 442 | /* Begin PBXProject section */ 443 | 2C0E159E13589B0B004096C7 /* Project object */ = { 444 | isa = PBXProject; 445 | attributes = { 446 | LastTestingUpgradeCheck = 0610; 447 | LastUpgradeCheck = 0610; 448 | ORGANIZATIONNAME = "Chalmers Göteborg"; 449 | }; 450 | buildConfigurationList = 2C0E15A113589B0B004096C7 /* Build configuration list for PBXProject "PDFKitten" */; 451 | compatibilityVersion = "Xcode 3.2"; 452 | developmentRegion = English; 453 | hasScannedForEncodings = 0; 454 | knownRegions = ( 455 | en, 456 | ); 457 | mainGroup = 2C0E159C13589B0B004096C7; 458 | productRefGroup = 2C0E15A813589B0B004096C7 /* Products */; 459 | projectDirPath = ""; 460 | projectRoot = ""; 461 | targets = ( 462 | 2C0E15A613589B0B004096C7 /* PDFKitten */, 463 | 2C35AD6D167E02E000BA5F79 /* PDFKittenTests */, 464 | ); 465 | }; 466 | /* End PBXProject section */ 467 | 468 | /* Begin PBXResourcesBuildPhase section */ 469 | 2C0E15A513589B0B004096C7 /* Resources */ = { 470 | isa = PBXResourcesBuildPhase; 471 | buildActionMask = 2147483647; 472 | files = ( 473 | 2CDC3F80138977C1006CC6FB /* InfoPlist.strings in Resources */, 474 | 2CDC3F81138977C1006CC6FB /* MainWindow.xib in Resources */, 475 | 2CA0FCA613EEB1C00028DCCD /* PageViewController.xib in Resources */, 476 | 2C77F7B41442381300767E6A /* Kurt the Cat.pdf in Resources */, 477 | ); 478 | runOnlyForDeploymentPostprocessing = 0; 479 | }; 480 | 2C35AD6B167E02E000BA5F79 /* Resources */ = { 481 | isa = PBXResourcesBuildPhase; 482 | buildActionMask = 2147483647; 483 | files = ( 484 | 2C1BB5CE168B97960026F266 /* KurtStory.txt in Resources */, 485 | 2C35AD77167E02E000BA5F79 /* InfoPlist.strings in Resources */, 486 | ); 487 | runOnlyForDeploymentPostprocessing = 0; 488 | }; 489 | /* End PBXResourcesBuildPhase section */ 490 | 491 | /* Begin PBXShellScriptBuildPhase section */ 492 | 2C35AD6C167E02E000BA5F79 /* ShellScript */ = { 493 | isa = PBXShellScriptBuildPhase; 494 | buildActionMask = 2147483647; 495 | files = ( 496 | ); 497 | inputPaths = ( 498 | ); 499 | outputPaths = ( 500 | ); 501 | runOnlyForDeploymentPostprocessing = 0; 502 | shellPath = /bin/sh; 503 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 504 | }; 505 | /* End PBXShellScriptBuildPhase section */ 506 | 507 | /* Begin PBXSourcesBuildPhase section */ 508 | 2C0E15A313589B0B004096C7 /* Sources */ = { 509 | isa = PBXSourcesBuildPhase; 510 | buildActionMask = 2147483647; 511 | files = ( 512 | 2CE7BC9D13896EF0009784B9 /* PageView.m in Sources */, 513 | 2CE7BCAF13896EFB009784B9 /* main.m in Sources */, 514 | 2CE7BCB113896EFB009784B9 /* PDFKittenAppDelegate.m in Sources */, 515 | 2CE7BCC413897135009784B9 /* CIDType0Font.m in Sources */, 516 | 2CE7BCC513897135009784B9 /* CIDType2Font.m in Sources */, 517 | 2CE7BCC613897135009784B9 /* CMap.m in Sources */, 518 | 2CE7BCC713897135009784B9 /* CompositeFont.m in Sources */, 519 | 2CE7BCCE1389713D009784B9 /* Font.m in Sources */, 520 | 2CE7BCCF1389713D009784B9 /* FontCollection.m in Sources */, 521 | 2CE7BCD01389713D009784B9 /* FontDescriptor.m in Sources */, 522 | 2CE7BCD313897142009784B9 /* MMType1Font.m in Sources */, 523 | 2CE7BCD61389714F009784B9 /* SimpleFont.m in Sources */, 524 | 2CE7BCDF13897159009784B9 /* TrueTypeFont.m in Sources */, 525 | 2CE7BCE013897159009784B9 /* Type0Font.m in Sources */, 526 | 2CE7BCE113897159009784B9 /* Type1Font.m in Sources */, 527 | 2CE7BCE213897159009784B9 /* Type3Font.m in Sources */, 528 | 2CE7BCE713897245009784B9 /* Scanner.m in Sources */, 529 | 2CE7BCEB1389725D009784B9 /* RenderingState.m in Sources */, 530 | 2CE7BCEF13897294009784B9 /* Selection.m in Sources */, 531 | 2CDC3F65138972E5006CC6FB /* StringDetector.m in Sources */, 532 | 2CA0FC9413EEB11B0028DCCD /* RootViewController.m in Sources */, 533 | 2CA0FC9713EEB1210028DCCD /* PDFPage.m in Sources */, 534 | 2CA0FCA113EEB1B80028DCCD /* Page.m in Sources */, 535 | 2CA0FCA513EEB1C00028DCCD /* PageViewController.m in Sources */, 536 | 2C47505F13F3E48200BC2DB9 /* DocumentsView.m in Sources */, 537 | 2CC77EC114560E2E000E9B8E /* PDFPageDetailsView.m in Sources */, 538 | 2C06404314C782DF00A9B33F /* FontFile.m in Sources */, 539 | 2C265927154D9C3900E11745 /* RenderingStateStack.m in Sources */, 540 | 2C209A9215C2B36900D4437B /* CIDFont.m in Sources */, 541 | 2CB5B24D168CF50E009103E6 /* pdfScannerCallbacks.mm in Sources */, 542 | ); 543 | runOnlyForDeploymentPostprocessing = 0; 544 | }; 545 | 2C35AD69167E02E000BA5F79 /* Sources */ = { 546 | isa = PBXSourcesBuildPhase; 547 | buildActionMask = 2147483647; 548 | files = ( 549 | 2C35AD83167E055A00BA5F79 /* StringDetectorTest.m in Sources */, 550 | ); 551 | runOnlyForDeploymentPostprocessing = 0; 552 | }; 553 | /* End PBXSourcesBuildPhase section */ 554 | 555 | /* Begin PBXTargetDependency section */ 556 | 2C35AD80167E035B00BA5F79 /* PBXTargetDependency */ = { 557 | isa = PBXTargetDependency; 558 | target = 2C0E15A613589B0B004096C7 /* PDFKitten */; 559 | targetProxy = 2C35AD7F167E035B00BA5F79 /* PBXContainerItemProxy */; 560 | }; 561 | /* End PBXTargetDependency section */ 562 | 563 | /* Begin PBXVariantGroup section */ 564 | 2C35AD75167E02E000BA5F79 /* InfoPlist.strings */ = { 565 | isa = PBXVariantGroup; 566 | children = ( 567 | 2C35AD76167E02E000BA5F79 /* en */, 568 | ); 569 | name = InfoPlist.strings; 570 | sourceTree = ""; 571 | }; 572 | 2CDC3F7A138977C0006CC6FB /* InfoPlist.strings */ = { 573 | isa = PBXVariantGroup; 574 | children = ( 575 | 2CDC3F7B138977C0006CC6FB /* en */, 576 | ); 577 | name = InfoPlist.strings; 578 | sourceTree = ""; 579 | }; 580 | 2CDC3F7C138977C0006CC6FB /* MainWindow.xib */ = { 581 | isa = PBXVariantGroup; 582 | children = ( 583 | 2CDC3F7D138977C1006CC6FB /* en */, 584 | ); 585 | name = MainWindow.xib; 586 | sourceTree = ""; 587 | }; 588 | /* End PBXVariantGroup section */ 589 | 590 | /* Begin XCBuildConfiguration section */ 591 | 2C0E15DC13589B0C004096C7 /* Debug */ = { 592 | isa = XCBuildConfiguration; 593 | buildSettings = { 594 | GCC_C_LANGUAGE_STANDARD = gnu99; 595 | GCC_OPTIMIZATION_LEVEL = 0; 596 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 597 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 598 | GCC_VERSION = ""; 599 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 600 | GCC_WARN_UNUSED_VARIABLE = YES; 601 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 602 | ONLY_ACTIVE_ARCH = YES; 603 | SDKROOT = iphoneos; 604 | TARGETED_DEVICE_FAMILY = 2; 605 | }; 606 | name = Debug; 607 | }; 608 | 2C0E15DD13589B0C004096C7 /* Release */ = { 609 | isa = XCBuildConfiguration; 610 | buildSettings = { 611 | GCC_C_LANGUAGE_STANDARD = gnu99; 612 | GCC_VERSION = ""; 613 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 614 | GCC_WARN_UNUSED_VARIABLE = YES; 615 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 616 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 617 | SDKROOT = iphoneos; 618 | TARGETED_DEVICE_FAMILY = 2; 619 | }; 620 | name = Release; 621 | }; 622 | 2C0E15DF13589B0C004096C7 /* Debug */ = { 623 | isa = XCBuildConfiguration; 624 | buildSettings = { 625 | ALWAYS_SEARCH_USER_PATHS = NO; 626 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 627 | CLANG_CXX_LIBRARY = "libc++"; 628 | CLANG_ENABLE_MODULES = YES; 629 | CLANG_ENABLE_OBJC_ARC = YES; 630 | COPY_PHASE_STRIP = NO; 631 | FRAMEWORK_SEARCH_PATHS = ( 632 | "$(inherited)", 633 | "\"$(SRCROOT)\"", 634 | ); 635 | GCC_DYNAMIC_NO_PIC = NO; 636 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 637 | GCC_PREFIX_HEADER = "PDFKitten/PDFKitten-Prefix.pch"; 638 | GCC_VERSION = ""; 639 | GENERATE_PKGINFO_FILE = NO; 640 | INFOPLIST_FILE = "PDFKitten/PDFKitten-Info.plist"; 641 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 642 | PRODUCT_NAME = PDFKitten; 643 | WRAPPER_EXTENSION = app; 644 | }; 645 | name = Debug; 646 | }; 647 | 2C0E15E013589B0C004096C7 /* Release */ = { 648 | isa = XCBuildConfiguration; 649 | buildSettings = { 650 | ALWAYS_SEARCH_USER_PATHS = NO; 651 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 652 | CLANG_CXX_LIBRARY = "libc++"; 653 | CLANG_ENABLE_MODULES = YES; 654 | CLANG_ENABLE_OBJC_ARC = YES; 655 | COPY_PHASE_STRIP = YES; 656 | FRAMEWORK_SEARCH_PATHS = ( 657 | "$(inherited)", 658 | "\"$(SRCROOT)\"", 659 | ); 660 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 661 | GCC_PREFIX_HEADER = "PDFKitten/PDFKitten-Prefix.pch"; 662 | GCC_VERSION = ""; 663 | GENERATE_PKGINFO_FILE = NO; 664 | INFOPLIST_FILE = "PDFKitten/PDFKitten-Info.plist"; 665 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 666 | PRODUCT_NAME = PDFKitten; 667 | VALIDATE_PRODUCT = YES; 668 | WRAPPER_EXTENSION = app; 669 | }; 670 | name = Release; 671 | }; 672 | 2C35AD7D167E02E000BA5F79 /* Debug */ = { 673 | isa = XCBuildConfiguration; 674 | buildSettings = { 675 | ALWAYS_SEARCH_USER_PATHS = NO; 676 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PDFKitten.app/PDFKitten"; 677 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 678 | CLANG_CXX_LIBRARY = "libc++"; 679 | CLANG_WARN_EMPTY_BODY = YES; 680 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 681 | COPY_PHASE_STRIP = NO; 682 | FRAMEWORK_SEARCH_PATHS = ( 683 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 684 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 685 | "$(inherited)", 686 | ); 687 | GCC_DYNAMIC_NO_PIC = NO; 688 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 689 | GCC_PREFIX_HEADER = "PDFKittenTests/PDFKittenTests-Prefix.pch"; 690 | GCC_PREPROCESSOR_DEFINITIONS = ( 691 | "DEBUG=1", 692 | "$(inherited)", 693 | ); 694 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 695 | INFOPLIST_FILE = "PDFKittenTests/PDFKittenTests-Info.plist"; 696 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 697 | ONLY_ACTIVE_ARCH = YES; 698 | PRODUCT_NAME = "$(TARGET_NAME)"; 699 | TEST_HOST = "$(BUNDLE_LOADER)"; 700 | }; 701 | name = Debug; 702 | }; 703 | 2C35AD7E167E02E000BA5F79 /* Release */ = { 704 | isa = XCBuildConfiguration; 705 | buildSettings = { 706 | ALWAYS_SEARCH_USER_PATHS = NO; 707 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PDFKitten.app/PDFKitten"; 708 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 709 | CLANG_CXX_LIBRARY = "libc++"; 710 | CLANG_WARN_EMPTY_BODY = YES; 711 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 712 | COPY_PHASE_STRIP = YES; 713 | FRAMEWORK_SEARCH_PATHS = ( 714 | "\"$(SDKROOT)/Developer/Library/Frameworks\"", 715 | "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", 716 | "$(inherited)", 717 | ); 718 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 719 | GCC_PREFIX_HEADER = "PDFKittenTests/PDFKittenTests-Prefix.pch"; 720 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 721 | INFOPLIST_FILE = "PDFKittenTests/PDFKittenTests-Info.plist"; 722 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 723 | PRODUCT_NAME = "$(TARGET_NAME)"; 724 | TEST_HOST = "$(BUNDLE_LOADER)"; 725 | VALIDATE_PRODUCT = YES; 726 | }; 727 | name = Release; 728 | }; 729 | /* End XCBuildConfiguration section */ 730 | 731 | /* Begin XCConfigurationList section */ 732 | 2C0E15A113589B0B004096C7 /* Build configuration list for PBXProject "PDFKitten" */ = { 733 | isa = XCConfigurationList; 734 | buildConfigurations = ( 735 | 2C0E15DC13589B0C004096C7 /* Debug */, 736 | 2C0E15DD13589B0C004096C7 /* Release */, 737 | ); 738 | defaultConfigurationIsVisible = 0; 739 | defaultConfigurationName = Debug; 740 | }; 741 | 2C0E15DE13589B0C004096C7 /* Build configuration list for PBXNativeTarget "PDFKitten" */ = { 742 | isa = XCConfigurationList; 743 | buildConfigurations = ( 744 | 2C0E15DF13589B0C004096C7 /* Debug */, 745 | 2C0E15E013589B0C004096C7 /* Release */, 746 | ); 747 | defaultConfigurationIsVisible = 0; 748 | defaultConfigurationName = Debug; 749 | }; 750 | 2C35AD7C167E02E000BA5F79 /* Build configuration list for PBXNativeTarget "PDFKittenTests" */ = { 751 | isa = XCConfigurationList; 752 | buildConfigurations = ( 753 | 2C35AD7D167E02E000BA5F79 /* Debug */, 754 | 2C35AD7E167E02E000BA5F79 /* Release */, 755 | ); 756 | defaultConfigurationIsVisible = 0; 757 | defaultConfigurationName = Debug; 758 | }; 759 | /* End XCConfigurationList section */ 760 | }; 761 | rootObject = 2C0E159E13589B0B004096C7 /* Project object */; 762 | } 763 | -------------------------------------------------------------------------------- /PDFKitten.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PDFKitten.xcodeproj/project.xcworkspace/xcshareddata/PDFKitten.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | B8CCD77E-B758-4A95-9C30-A9D34704B18A 9 | IDESourceControlProjectName 10 | PDFKitten 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | DD37BB420EBB61E23BECC947958601C145484998 14 | https://github.com/battatech/PDFKitten.git 15 | 16 | IDESourceControlProjectPath 17 | PDFKitten.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | DD37BB420EBB61E23BECC947958601C145484998 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/battatech/PDFKitten.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | DD37BB420EBB61E23BECC947958601C145484998 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | DD37BB420EBB61E23BECC947958601C145484998 36 | IDESourceControlWCCName 37 | PDFKitten 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PDFKitten.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PDFKitten/CIDFont.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CompositeFont.h" 3 | 4 | @interface CIDFont : CompositeFont { 5 | BOOL identity; 6 | } 7 | 8 | @property (readonly, getter = isIdentity) BOOL identity; 9 | @end 10 | -------------------------------------------------------------------------------- /PDFKitten/CIDFont.m: -------------------------------------------------------------------------------- 1 | #import "CIDFont.h" 2 | 3 | @implementation CIDFont 4 | 5 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 6 | { 7 | unichar *characterIDs = (unichar *) CGPDFStringGetBytePtr(pdfString); 8 | int length = (int)(CGPDFStringGetLength(pdfString) / sizeof(unichar)); 9 | int magicalOffset = ([self isIdentity] ? 0 : 30); 10 | NSMutableString *unicodeString = [NSMutableString string]; 11 | for (int i = 0; i < length; i++) 12 | { 13 | unichar unicodeValue = characterIDs[i] + magicalOffset; 14 | [unicodeString appendFormat:@"%C", unicodeValue]; 15 | } 16 | 17 | return unicodeString; 18 | } 19 | 20 | @synthesize identity; 21 | @end 22 | -------------------------------------------------------------------------------- /PDFKitten/CIDType0Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CIDFont.h" 3 | 4 | @interface CIDType0Font : CIDFont { 5 | 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/CIDType0Font.m: -------------------------------------------------------------------------------- 1 | #import "CIDType0Font.h" 2 | 3 | 4 | @implementation CIDType0Font 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /PDFKitten/CIDType2Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CIDFont.h" 3 | 4 | @interface CIDType2Font : CIDFont { 5 | NSData *cidGidMap; 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/CIDType2Font.m: -------------------------------------------------------------------------------- 1 | #import "CIDType2Font.h" 2 | 3 | 4 | @implementation CIDType2Font 5 | 6 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 7 | { 8 | if (self = [super initWithFontDictionary:dict]) 9 | { 10 | // Type 2 CID font only: set CID/GID mapping 11 | CGPDFObjectRef streamOrName = nil; 12 | 13 | if (CGPDFDictionaryGetObject(dict, "CIDToGIDMap", &streamOrName)) 14 | { 15 | CGPDFObjectType type = CGPDFObjectGetType(streamOrName); 16 | identity = (type == kCGPDFObjectTypeName); 17 | 18 | if (type == kCGPDFObjectTypeStream) 19 | { 20 | CGPDFStreamRef stream = nil; 21 | if (CGPDFObjectGetValue(streamOrName, kCGPDFObjectTypeStream, &stream)) 22 | { 23 | cidGidMap = (__bridge NSData *) CGPDFStreamCopyData(stream, nil); 24 | } 25 | } 26 | } 27 | } 28 | 29 | return self; 30 | } 31 | 32 | - (unichar)gidWithCid:(unsigned char)cid 33 | { 34 | void *gid = nil; 35 | [cidGidMap getBytes:gid range:NSMakeRange(cid * 2, 2)]; 36 | return (unichar) gid; 37 | } 38 | 39 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 40 | { 41 | size_t length = CGPDFStringGetLength(pdfString); 42 | const unsigned char *cid = CGPDFStringGetBytePtr(pdfString); 43 | NSMutableString *result = [[NSMutableString alloc] init]; 44 | NSData *data = [NSData dataWithBytes:cid length:length]; 45 | NSLog(@"%@", data); 46 | for (int i = 0; i < length; i+=2) { 47 | unsigned char unicodeValue1 = cid[i]; 48 | unsigned char unicodeValue2 = cid[i+1]; 49 | unichar unicodeValue = (unicodeValue1 << 8) + unicodeValue2; 50 | [result appendFormat:@"%C", unicodeValue]; 51 | } 52 | return result; 53 | } 54 | 55 | /* 56 | 57 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 58 | { 59 | if (self.identity) 60 | { 61 | // Use 2-byte CIDToGID identity mapping 62 | size_t length = CGPDFStringGetLength(pdfString); 63 | const unsigned char *cid = CGPDFStringGetBytePtr(pdfString); 64 | 65 | 66 | NSData *data = [NSData dataWithBytes:cid length:length]; 67 | NSLog(@"%@", data); 68 | 69 | for (int i = 0; i < length; i+=2) 70 | { 71 | unichar unicodeValue = cid[i] << 8 | cid[i+1]; 72 | // unichar unicodeValue = 0x4ea4; 73 | NSLog(@"%C %x", unicodeValue, unicodeValue); 74 | } 75 | 76 | } 77 | else 78 | { 79 | 80 | } 81 | 82 | 83 | return @""; 84 | } 85 | */ 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /PDFKitten/CMap.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern NSValue *rangeValue(unsigned int from, unsigned int to); 4 | 5 | @interface Operator : NSObject 6 | + (Operator *)operatorWithStart:(NSString *)start end:(NSString *)end handler:(SEL)handler; 7 | @property (retain) NSString *start; 8 | @property (retain) NSString *end; 9 | @property SEL handler; 10 | @end 11 | 12 | @interface CMap : NSObject { 13 | NSMutableDictionary *context; 14 | 15 | /* CMap ranges */ 16 | NSMutableArray *codeSpaceRanges; 17 | 18 | /* Character mappings */ 19 | NSMutableDictionary *characterMappings; 20 | 21 | /* Character range mappings */ 22 | NSMutableDictionary *characterRangeMappings; 23 | } 24 | 25 | /* Initialize with PDF stream containing a CMap */ 26 | - (id)initWithPDFStream:(CGPDFStreamRef)stream; 27 | 28 | /* Initialize with a string representation of a CMap */ 29 | - (id)initWithString:(NSString *)string; 30 | 31 | /* Unicode mapping for character ID */ 32 | - (unichar)unicodeCharacter:(unichar)cid; 33 | 34 | - (unichar)cidCharacter:(unichar)unicode; 35 | 36 | @property (nonatomic, retain) NSMutableArray *codeSpaceRanges; 37 | @property (nonatomic, retain) NSMutableDictionary *characterMappings; 38 | @property (nonatomic, retain) NSMutableDictionary *characterRangeMappings; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /PDFKitten/CMap.m: -------------------------------------------------------------------------------- 1 | #import "CMap.h" 2 | 3 | static NSSet *sharedOperators = nil; 4 | static NSCharacterSet *sharedTagSet = nil; 5 | static NSCharacterSet *sharedTokenDelimimerSet = nil; 6 | static NSString *kOperatorKey = @"CurrentOperator"; 7 | 8 | NSValue *rangeValue(unsigned int from, unsigned int to) 9 | { 10 | return [NSValue valueWithRange:NSMakeRange(from, to-from)]; 11 | } 12 | 13 | @implementation Operator 14 | 15 | + (Operator *)operatorWithStart:(NSString *)start end:(NSString *)end handler:(SEL)handler 16 | { 17 | Operator *op = [[Operator alloc] init]; 18 | op.start = start; 19 | op.end = end; 20 | op.handler = handler; 21 | return op; 22 | } 23 | 24 | @synthesize start, end, handler; 25 | 26 | @end 27 | 28 | @interface CMap () 29 | 30 | - (void)handleCodeSpaceRange:(NSString *)string; 31 | - (void)handleCharacter:(NSString *)string; 32 | - (void)handleCharacterRange:(NSString *)string; 33 | - (void)parse:(NSString *)cMapString; 34 | @property(nonatomic, retain) NSMutableDictionary *context; 35 | @property(readonly) NSCharacterSet *tokenDelimiterSet; 36 | @property(readonly) NSCharacterSet *tagSet; 37 | @property(readonly) NSSet *operators; 38 | 39 | @end 40 | 41 | @implementation CMap 42 | 43 | - (id)initWithString:(NSString *)string 44 | { 45 | if ((self = [super init])) 46 | { 47 | [self parse:string]; 48 | } 49 | return self; 50 | } 51 | 52 | - (id)initWithPDFStream:(CGPDFStreamRef)stream 53 | { 54 | NSData *data = (__bridge NSData *) CGPDFStreamCopyData(stream, nil); 55 | NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 56 | id obj = [self initWithString:text]; 57 | return obj; 58 | } 59 | 60 | - (BOOL)isInCodeSpaceRange:(unichar)cid 61 | { 62 | for (NSValue *rangeValue in self.codeSpaceRanges) 63 | { 64 | NSRange range = [rangeValue rangeValue]; 65 | if (cid >= range.location && cid <= NSMaxRange(range)) 66 | { 67 | return YES; 68 | } 69 | } 70 | return NO; 71 | } 72 | 73 | #pragma mark - Public methods 74 | 75 | /**! 76 | * Returns the unicode value mapped by the given character ID 77 | */ 78 | - (unichar)unicodeCharacter:(unichar)cid 79 | { 80 | if (![self isInCodeSpaceRange:cid]) return (unichar) NSNotFound; 81 | 82 | NSArray *mappedRanges = [self.characterRangeMappings allKeys]; 83 | for (NSValue *rangeValue in mappedRanges) 84 | { 85 | NSRange range = [rangeValue rangeValue]; 86 | if (cid >= range.location && cid <= NSMaxRange(range)) 87 | { 88 | NSNumber *offsetValue = [self.characterRangeMappings objectForKey:rangeValue]; 89 | return cid + [offsetValue intValue]; 90 | } 91 | } 92 | 93 | NSNumber *result = [self.characterMappings objectForKey:[NSNumber numberWithInt:cid]]; 94 | if (result) { 95 | return [result intValue]; 96 | } 97 | 98 | return (unichar) NSNotFound; 99 | } 100 | 101 | - (unichar)cidCharacter:(unichar)unicode { 102 | __block unichar result = NSNotFound; 103 | 104 | [self.characterRangeMappings enumerateKeysAndObjectsUsingBlock:^(NSValue *rangeValue, NSNumber *offset, BOOL *stop) { 105 | NSRange range = [rangeValue rangeValue]; 106 | range.location += [offset intValue]; 107 | if (unicode >= range.location && unicode <= NSMaxRange(range)) { 108 | result = unicode - [offset intValue]; 109 | *stop = YES; 110 | } 111 | }]; 112 | if (result != NSNotFound) 113 | return result; 114 | 115 | NSArray *keys = [self.characterMappings allKeysForObject:[NSNumber numberWithInt:unicode]]; 116 | if (keys.count) { 117 | if (keys.count > 1) { 118 | NSLog(@"more keys for character %C keys = %@", unicode, keys); 119 | } 120 | return [[keys lastObject]intValue]; 121 | } else { 122 | return NSNotFound; 123 | } 124 | /* 125 | // Look up the offsets dictionary for this unicode 126 | for (NSDictionary *dict in offsets) 127 | { 128 | int firstChar = [[dict objectForKey:@"First"] intValue]; 129 | int lastChar = [[dict objectForKey:@"Last"] intValue]; 130 | int offset = [[dict objectForKey:@"Offset"] intValue]; 131 | 132 | for (int i = 0 ; i <= lastChar-firstChar ; i++) { 133 | unichar dictUnicode = offset+i; 134 | if (dictUnicode == unicode) { 135 | return i; 136 | } 137 | } 138 | } */ 139 | } 140 | 141 | #pragma mark - 142 | #pragma mark Scanner 143 | 144 | - (Operator *)operatorWithStartingToken:(NSString *)token { 145 | if (token) { 146 | for (Operator *op in self.operators) { 147 | if ([op.start isEqualToString:token]) { 148 | return op; 149 | } 150 | } 151 | } 152 | return nil; 153 | } 154 | 155 | /**! 156 | * Returns the next token that is not a comment. Only remainder-of-line comments are supported. 157 | * The scanner is advanced to past the returned token. 158 | * 159 | * @param scanner a scanner 160 | * @return next non-comment token 161 | */ 162 | - (NSString *)tokenByTrimmingComments:(NSScanner *)scanner 163 | { 164 | NSString *token = nil; 165 | [scanner scanUpToCharactersFromSet:self.tokenDelimiterSet intoString:&token]; 166 | 167 | if ( !token ) 168 | { 169 | // There's no token delimiters left in the scanner, so we need to scan to the end. 170 | 171 | // Get the remainder of the scanner's string 172 | token = [[scanner string] substringFromIndex:[scanner scanLocation]]; 173 | // Advance the scanner to the end of the string 174 | [scanner scanString:token intoString:nil]; 175 | } 176 | 177 | static NSString *commentMarker = @"%%"; 178 | NSRange commentMarkerRange = [token rangeOfString:commentMarker]; 179 | if (commentMarkerRange.location != NSNotFound) 180 | { 181 | [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:nil]; 182 | token = [token substringToIndex:commentMarkerRange.location]; 183 | if (token.length == 0) 184 | { 185 | return [self tokenByTrimmingComments:scanner]; 186 | } 187 | } 188 | 189 | return token; 190 | } 191 | 192 | /**! 193 | * Parse a CMap. 194 | * 195 | * @param cMapString string representation of a CMap 196 | */ 197 | - (void)parse:(NSString *)cMapString 198 | { 199 | NSScanner *scanner = [NSScanner scannerWithString:cMapString]; 200 | NSString *token = nil; 201 | while (![scanner isAtEnd]) 202 | { 203 | token = [self tokenByTrimmingComments:scanner]; 204 | 205 | Operator *operator = [self operatorWithStartingToken:token]; 206 | if (operator) 207 | { 208 | // Start a new context 209 | self.context = [NSMutableDictionary dictionaryWithObject:operator forKey:kOperatorKey]; 210 | } 211 | else if (self.context) 212 | { 213 | operator = [self.context valueForKey:kOperatorKey]; 214 | if ([token isEqualToString:operator.end]) 215 | { 216 | // End the current context 217 | self.context = nil; 218 | } 219 | else 220 | { 221 | // Send input to the current context 222 | [self performSelector:operator.handler withObject:token]; 223 | } 224 | } 225 | } 226 | } 227 | 228 | 229 | #pragma mark - 230 | #pragma mark Parsing handlers 231 | 232 | /**! 233 | * Trims tag characters from the argument string, and returns the parsed integer value of the string. 234 | * 235 | * @param tagString string representing a hexadecimal number, possibly within tags 236 | */ 237 | - (unsigned int)valueOfTag:(NSString *)tagString 238 | { 239 | unsigned int numericValue = 0; 240 | tagString = [tagString stringByTrimmingCharactersInSet:self.tagSet]; 241 | [[NSScanner scannerWithString:tagString] scanHexInt:&numericValue]; 242 | return numericValue; 243 | } 244 | 245 | /**! 246 | * Code space ranges are pairs of hex numbers: 247 | * 248 | */ 249 | - (void)handleCodeSpaceRange:(NSString *)string 250 | { 251 | static NSString *rangeLowerBound = @"MIN"; 252 | NSNumber *value = [NSNumber numberWithInt:[self valueOfTag:string]]; 253 | NSNumber *low = [self.context valueForKey:rangeLowerBound]; 254 | 255 | if (!low) 256 | { 257 | [self.context setValue:value forKey:rangeLowerBound]; 258 | return; 259 | } 260 | 261 | [self.codeSpaceRanges addObject:rangeValue([low intValue], [value intValue])]; 262 | [self.context removeObjectForKey:rangeLowerBound]; 263 | } 264 | 265 | /**! 266 | * Character mappings appear in pairs: 267 | * 268 | */ 269 | - (void)handleCharacter:(NSString *)character 270 | { 271 | NSNumber *value = [NSNumber numberWithInt:[self valueOfTag:character]]; 272 | static NSString *origin = @"Origin"; 273 | NSNumber *from = [self.context valueForKey:origin]; 274 | 275 | if (!from) 276 | { 277 | [self.context setValue:value forKey:origin]; 278 | return; 279 | } 280 | 281 | [self.characterMappings setObject:value forKey:from]; 282 | [self.context removeObjectForKey:origin]; 283 | } 284 | 285 | /**! 286 | * Ranges appear on the triplet form: 287 | * 288 | */ 289 | - (void)handleCharacterRange:(NSString *)token 290 | { 291 | NSNumber *value = [NSNumber numberWithInt:[self valueOfTag:token]]; 292 | static NSString *from = @"From"; 293 | static NSString *to = @"To"; 294 | NSNumber *fromValue = [self.context valueForKey:from]; 295 | NSNumber *toValue = [self.context valueForKey:to]; 296 | 297 | if (!fromValue) 298 | { 299 | [self.context setValue:value forKey:from]; 300 | return; 301 | } 302 | else if (!toValue) 303 | { 304 | [self.context setValue:value forKey:to]; 305 | return; 306 | } 307 | 308 | NSValue *range = rangeValue([fromValue intValue], [toValue intValue]); 309 | 310 | [self.characterRangeMappings setObject:value forKey:range]; 311 | [self.context removeObjectForKey:from]; 312 | [self.context removeObjectForKey:to]; 313 | } 314 | 315 | #pragma mark - 316 | #pragma mark Accessor methods 317 | 318 | - (NSSet *)operators { 319 | if (!sharedOperators) { 320 | sharedOperators = [[NSMutableSet alloc] initWithObjects: 321 | [Operator operatorWithStart:@"begincodespacerange" 322 | end:@"endcodespacerange" 323 | handler:@selector(handleCodeSpaceRange:)], 324 | [Operator operatorWithStart:@"beginbfchar" 325 | end:@"endbfchar" 326 | handler:@selector(handleCharacter:)], 327 | [Operator operatorWithStart:@"beginbfrange" 328 | end:@"endbfrange" 329 | handler:@selector(handleCharacterRange:)], 330 | nil]; 331 | } 332 | return sharedOperators; 333 | } 334 | 335 | - (NSCharacterSet *)tagSet 336 | { 337 | if (!sharedTagSet) 338 | { 339 | sharedTagSet = [NSCharacterSet characterSetWithCharactersInString:@"<>"]; 340 | } 341 | 342 | return sharedTagSet; 343 | } 344 | 345 | - (NSCharacterSet *)tokenDelimiterSet 346 | { 347 | if (!sharedTokenDelimimerSet) 348 | { 349 | sharedTokenDelimimerSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 350 | } 351 | 352 | return sharedTokenDelimimerSet; 353 | } 354 | 355 | - (NSMutableArray *)codeSpaceRanges 356 | { 357 | if (!codeSpaceRanges) 358 | { 359 | codeSpaceRanges = [[NSMutableArray alloc] init]; 360 | } 361 | 362 | return codeSpaceRanges; 363 | } 364 | 365 | - (NSMutableDictionary *)characterMappings 366 | { 367 | if (!characterMappings) 368 | { 369 | characterMappings = [[NSMutableDictionary alloc] init]; 370 | } 371 | 372 | return characterMappings; 373 | } 374 | 375 | - (NSMutableDictionary *)characterRangeMappings 376 | { 377 | if (!characterRangeMappings) 378 | { 379 | self.characterRangeMappings = [NSMutableDictionary dictionary]; 380 | } 381 | 382 | return characterRangeMappings; 383 | } 384 | 385 | @synthesize context; 386 | @synthesize codeSpaceRanges, characterMappings, characterRangeMappings; 387 | 388 | @end -------------------------------------------------------------------------------- /PDFKitten/CompositeFont.h: -------------------------------------------------------------------------------- 1 | /* 2 | * A composite font is one of the following types: 3 | * - Type0 4 | * - CIDType0Font 5 | * - CIDType2Font 6 | * 7 | * Composite fonts have the following specific traits: 8 | * - Default glyph width 9 | * 10 | */ 11 | 12 | #import 13 | #import "Font.h" 14 | 15 | @interface CompositeFont : Font { 16 | CGFloat defaultWidth; 17 | } 18 | 19 | @property (nonatomic, assign) CGFloat defaultWidth; 20 | @end 21 | -------------------------------------------------------------------------------- /PDFKitten/CompositeFont.m: -------------------------------------------------------------------------------- 1 | #import "CompositeFont.h" 2 | 3 | @implementation CompositeFont 4 | 5 | /* Override with implementation for composite fonts */ 6 | - (void)setWidthsWithFontDictionary:(CGPDFDictionaryRef)dict 7 | { 8 | CGPDFArrayRef widthsArray; 9 | 10 | if (CGPDFDictionaryGetArray(dict, "W", &widthsArray)) 11 | { 12 | [self setWidthsWithArray:widthsArray]; 13 | } 14 | 15 | CGPDFInteger defaultWidthValue; 16 | 17 | if (CGPDFDictionaryGetInteger(dict, "DW", &defaultWidthValue)) 18 | { 19 | self.defaultWidth = defaultWidthValue; 20 | } 21 | } 22 | 23 | - (void)setWidthsWithArray:(CGPDFArrayRef)widthsArray 24 | { 25 | NSUInteger length = CGPDFArrayGetCount(widthsArray); 26 | int idx = 0; 27 | CGPDFObjectRef nextObject = nil; 28 | 29 | while (idx < length) 30 | { 31 | CGPDFInteger baseCid = 0; 32 | CGPDFArrayGetInteger(widthsArray, idx++, &baseCid); 33 | 34 | CGPDFObjectRef integerOrArray = nil; 35 | CGPDFInteger firstCharacter = 0; 36 | CGPDFArrayGetObject(widthsArray, idx++, &integerOrArray); 37 | 38 | if (CGPDFObjectGetType(integerOrArray) == kCGPDFObjectTypeInteger) 39 | { 40 | // [ first last width ] 41 | CGPDFInteger maxCid; 42 | CGPDFInteger glyphWidth; 43 | CGPDFObjectGetValue(integerOrArray, kCGPDFObjectTypeInteger, &maxCid); 44 | CGPDFArrayGetInteger(widthsArray, idx++, &glyphWidth); 45 | [self setWidthsFrom:baseCid to:maxCid width:glyphWidth]; 46 | 47 | // If the second item is an array, the sequence 48 | // defines widths on the form [ first list-of-widths ] 49 | CGPDFArrayRef characterWidths; 50 | 51 | if (!CGPDFObjectGetValue(nextObject, kCGPDFObjectTypeArray, &characterWidths)) 52 | { 53 | break; 54 | } 55 | 56 | NSUInteger widthsCount = CGPDFArrayGetCount(characterWidths); 57 | 58 | for (int index = 0; index < widthsCount ; index++) 59 | { 60 | CGPDFInteger width; 61 | 62 | if (CGPDFArrayGetInteger(characterWidths, index, &width)) 63 | { 64 | NSNumber *key = [NSNumber numberWithInt: (int)firstCharacter + index]; 65 | NSNumber *val = [NSNumber numberWithInt: (int)width]; 66 | [widths setObject:val forKey:key]; 67 | } 68 | } 69 | } 70 | else 71 | { 72 | // [ first list-of-widths ] 73 | CGPDFArrayRef glyphWidths; 74 | CGPDFObjectGetValue(integerOrArray, kCGPDFObjectTypeArray, &glyphWidths); 75 | [self setWidthsWithBase:baseCid array:glyphWidths]; 76 | } 77 | } 78 | } 79 | 80 | - (void)setWidthsFrom:(CGPDFInteger)cid to:(CGPDFInteger)maxCid width:(CGPDFInteger)width 81 | { 82 | while (cid <= maxCid) 83 | { 84 | [self.widths setObject:[NSNumber numberWithInt:(int)width] forKey:[NSNumber numberWithInt:(int)cid++]]; 85 | } 86 | } 87 | 88 | - (void)setWidthsWithBase:(CGPDFInteger)base array:(CGPDFArrayRef)array 89 | { 90 | NSInteger count = CGPDFArrayGetCount(array); 91 | CGPDFInteger width; 92 | 93 | for (int index = 0; index < count ; index++) 94 | { 95 | if (CGPDFArrayGetInteger(array, index, &width)) 96 | { 97 | [self.widths setObject:[NSNumber numberWithInt:(int)width] forKey:[NSNumber numberWithInt:(int)base + index]]; 98 | } 99 | } 100 | } 101 | 102 | - (CGFloat)widthOfCharacter:(unichar)characher withFontSize:(CGFloat)fontSize 103 | { 104 | NSNumber *width = [self.widths objectForKey:[NSNumber numberWithInt:characher - 30]]; 105 | 106 | if (!width) 107 | { 108 | return self.defaultWidth * fontSize; 109 | } 110 | 111 | return [width floatValue] * fontSize; 112 | } 113 | 114 | @synthesize defaultWidth; 115 | 116 | @end -------------------------------------------------------------------------------- /PDFKitten/DocumentsView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface DocumentsView : UINavigationController 4 | { 5 | UITableViewController *tableViewController; 6 | NSArray *documents; 7 | NSDictionary *urlsByName; 8 | 9 | __weak id delegate; 10 | } 11 | 12 | @property (nonatomic, weak) id delegate; 13 | 14 | @end -------------------------------------------------------------------------------- /PDFKitten/DocumentsView.m: -------------------------------------------------------------------------------- 1 | #import "DocumentsView.h" 2 | 3 | @implementation DocumentsView 4 | 5 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 6 | { 7 | // Return YES for supported orientations 8 | return YES; 9 | } 10 | 11 | - (void)loadDocuments 12 | { 13 | NSArray *userDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 14 | NSURL *docementsURL = [NSURL fileURLWithPath:[userDocuments lastObject]]; 15 | NSLog(@"%@", docementsURL); 16 | NSArray *documentsURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:docementsURL 17 | includingPropertiesForKeys:nil 18 | options:NSDirectoryEnumerationSkipsHiddenFiles 19 | error:nil]; 20 | 21 | NSMutableArray *names = [NSMutableArray array]; 22 | NSMutableDictionary *urls = [NSMutableDictionary dictionary]; 23 | 24 | NSArray *bundledResources = [[NSBundle mainBundle] URLsForResourcesWithExtension:@"pdf" subdirectory:nil]; 25 | documentsURLs = [documentsURLs arrayByAddingObjectsFromArray:bundledResources]; 26 | 27 | for (NSURL *docURL in documentsURLs) 28 | { 29 | NSString *title = [[docURL lastPathComponent] stringByDeletingPathExtension]; 30 | [names addObject:title]; 31 | [urls setObject:docURL forKey:title]; 32 | } 33 | 34 | documents = [[NSArray alloc] initWithArray:[names sortedArrayUsingSelector:@selector(compare:)]]; 35 | urlsByName = [[NSDictionary alloc] initWithDictionary:urls]; 36 | } 37 | 38 | - (void)viewDidLoad 39 | { 40 | tableViewController = [[UITableViewController alloc] init]; 41 | tableViewController.tableView.delegate = self; 42 | tableViewController.tableView.dataSource = self; 43 | tableViewController.navigationItem.title = @"Library"; 44 | 45 | [self loadDocuments]; 46 | [self pushViewController:tableViewController animated:NO]; 47 | } 48 | 49 | - (void)viewWillAppear:(BOOL)animated 50 | { 51 | [super viewWillAppear:animated]; 52 | [self setContentSizeForViewInPopover:CGSizeMake(100, 100)]; 53 | } 54 | 55 | #pragma mark UITableViewDelegate 56 | 57 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 58 | { 59 | return [documents count]; 60 | } 61 | 62 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 63 | { 64 | static NSString *identifier = @"CellIdentifier"; 65 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 66 | 67 | if (!cell) 68 | { 69 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]; 70 | } 71 | 72 | NSString *title = [documents objectAtIndex:indexPath.row]; 73 | cell.textLabel.text = title; 74 | cell.detailTextLabel.text = [[urlsByName objectForKey:title] relativePath]; 75 | 76 | return cell; 77 | } 78 | 79 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 80 | { 81 | NSString *title = [documents objectAtIndex:indexPath.row]; 82 | NSURL *url = [urlsByName objectForKey:title]; 83 | 84 | if ([delegate respondsToSelector:@selector(didSelectDocument:)]) 85 | { 86 | [delegate performSelector:@selector(didSelectDocument:) withObject:url]; 87 | } 88 | } 89 | 90 | @synthesize delegate; 91 | 92 | @end -------------------------------------------------------------------------------- /PDFKitten/Font.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Implements generic behavior of a font. 3 | * 4 | * Most likely used exclusively for subclassing, for Type 0, Type 1 etc. 5 | * 6 | * Ideally, the subclasses are hidden from the user, who interacts with them through this facade class. 7 | * 8 | */ 9 | #import 10 | #import "FontDescriptor.h" 11 | #import "CMap.h" 12 | 13 | extern const char *kFontDescriptorKey; 14 | extern const char *kTypeKey; 15 | 16 | typedef enum { 17 | UnknownEncoding = 0, 18 | StandardEncoding, // Defined in Type1 font programs 19 | MacRomanEncoding, 20 | WinAnsiEncoding, 21 | PDFDocEncoding, 22 | MacExpertEncoding, 23 | 24 | } CharacterEncoding; 25 | 26 | static inline NSStringEncoding nativeEncoding(CharacterEncoding encoding) 27 | { 28 | switch (encoding) { 29 | case MacRomanEncoding : 30 | return NSMacOSRomanStringEncoding; 31 | case WinAnsiEncoding : 32 | return NSWindowsCP1252StringEncoding; 33 | default: 34 | return NSUTF8StringEncoding; 35 | } 36 | } 37 | 38 | static inline BOOL knownEncoding(CharacterEncoding encoding) 39 | { 40 | return encoding > 0; 41 | } 42 | 43 | @interface Font : NSObject { 44 | CMap *toUnicode; 45 | NSMutableDictionary *widths; 46 | FontDescriptor *fontDescriptor; 47 | NSDictionary *ligatures; 48 | NSRange widthsRange; 49 | NSString *baseFont; 50 | CharacterEncoding encoding; 51 | } 52 | 53 | /* Factory method returns a Font object given a PDF font dictionary */ 54 | + (Font *)fontWithDictionary:(CGPDFDictionaryRef)dictionary; 55 | 56 | /* Initialize with a font dictionary */ 57 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict; 58 | 59 | /* Populate the widths array given font dictionary */ 60 | - (void)setWidthsWithFontDictionary:(CGPDFDictionaryRef)dict; 61 | 62 | /* Construct a font descriptor given font dictionary */ 63 | - (void)setFontDescriptorWithFontDictionary:(CGPDFDictionaryRef)dict; 64 | 65 | /* Given a PDF string, returns a Unicode string */ 66 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString; 67 | 68 | /* Given a PDF string, returns a CID string */ 69 | - (NSString *)cidWithPDFString:(CGPDFStringRef)pdfString; 70 | 71 | - (NSString *)unicodeWithPDFString:(CGPDFStringRef)pdfString; 72 | 73 | /* Returns the width of a charachter (optionally scaled to some font size) */ 74 | - (CGFloat)widthOfCharacter:(unichar)characher withFontSize:(CGFloat)fontSize; 75 | 76 | /* Import a ToUnicode CMap from a font dictionary */ 77 | - (void)setToUnicodeWithFontDictionary:(CGPDFDictionaryRef)dict; 78 | 79 | /* Return an equivalent string, replacing ligatures with individual characters */ 80 | - (NSString *)stringByExpandingLigatures:(NSString *)string; 81 | 82 | @property (nonatomic, retain) CMap *toUnicode; 83 | @property (nonatomic, retain) NSMutableDictionary *widths; 84 | @property (nonatomic, retain) FontDescriptor *fontDescriptor; 85 | @property (nonatomic, readonly) CGFloat minY; 86 | @property (nonatomic, readonly) CGFloat maxY; 87 | @property (nonatomic, readonly) NSDictionary *ligatures; 88 | @property (nonatomic, readonly) CGFloat widthOfSpace; 89 | @property (nonatomic, readonly) NSRange widthsRange; 90 | @property (nonatomic, assign) CharacterEncoding encoding; 91 | @property (nonatomic, readonly) NSArray *descendantFonts; 92 | 93 | /*! 94 | @property baseFont 95 | */ 96 | @property (nonatomic, retain) NSString *baseFont; 97 | 98 | /*! 99 | * The actual name of the base font, sans tag. 100 | @property baseFontName 101 | */ 102 | @property (nonatomic, readonly) NSString *baseFontName; 103 | @end 104 | -------------------------------------------------------------------------------- /PDFKitten/Font.m: -------------------------------------------------------------------------------- 1 | #import "Font.h" 2 | 3 | // Simple fonts 4 | #import "Type1Font.h" 5 | #import "TrueTypeFont.h" 6 | #import "MMType1Font.h" 7 | #import "Type3Font.h" 8 | 9 | // Composite fonts 10 | #import "Type0Font.h" 11 | #import "CIDType2Font.h" 12 | #import "CIDType0Font.h" 13 | 14 | const char *kType0Key = "Type0"; 15 | const char *kType1Key = "Type1"; 16 | const char *kMMType1Key = "MMType1"; 17 | const char *kType3Key = "Type3"; 18 | const char *kTrueTypeKey = "TrueType"; 19 | const char *kCidFontType0Key = "CIDFontType0"; 20 | const char *kCidFontType2Key = "CIDFontType2"; 21 | 22 | const char *kToUnicodeKey = "ToUnicode"; 23 | const char *kFontDescriptorKey = "FontDescriptor"; 24 | const char *kBaseFontKey = "BaseFont"; 25 | const char *kEncodingKey = "Encoding"; 26 | const char *kBaseEncodingKey = "BaseEncoding"; 27 | const char *kFontSubtypeKey = "Subtype"; 28 | const char *kFontKey = "Font"; 29 | const char *kTypeKey = "Type"; 30 | 31 | typedef const unsigned char CharacterCode; 32 | 33 | #pragma mark 34 | 35 | 36 | @implementation Font 37 | 38 | #pragma mark - Initialization 39 | 40 | /* Factory method returns a Font object given a PDF font dictionary */ 41 | + (Font *)fontWithDictionary:(CGPDFDictionaryRef)dictionary 42 | { 43 | const char *type = nil; 44 | CGPDFDictionaryGetName(dictionary, kTypeKey, &type); 45 | if (!type || strcmp(type, kFontKey) != 0) return nil; 46 | const char *subtype = nil; 47 | CGPDFDictionaryGetName(dictionary, kFontSubtypeKey, &subtype); 48 | 49 | Font *font = nil; 50 | if (!strcmp(subtype, kType0Key)) { 51 | font = [Type0Font alloc]; 52 | } 53 | else if (!strcmp(subtype, kType1Key)) { 54 | font = [Type1Font alloc]; 55 | } 56 | else if (!strcmp(subtype, kMMType1Key)) { 57 | font = [MMType1Font alloc]; 58 | } 59 | else if (!strcmp(subtype, kType3Key)) { 60 | font = [Type3Font alloc]; 61 | } 62 | else if (!strcmp(subtype, kTrueTypeKey)) { 63 | font = [TrueTypeFont alloc]; 64 | } 65 | else if (!strcmp(subtype, kCidFontType0Key)) { 66 | font = [CIDType0Font alloc]; 67 | } 68 | else if (!strcmp(subtype, kCidFontType2Key)) { 69 | font = [CIDType2Font alloc]; 70 | } 71 | 72 | [font initWithFontDictionary:dictionary]; 73 | return font; 74 | } 75 | 76 | /* Initialize with font dictionary */ 77 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 78 | { 79 | if ((self = [super init])) 80 | { 81 | // Populate the glyph widths store 82 | [self setWidthsWithFontDictionary:dict]; 83 | 84 | // Initialize the font descriptor 85 | [self setFontDescriptorWithFontDictionary:dict]; 86 | 87 | // Parse ToUnicode map 88 | [self setToUnicodeWithFontDictionary:dict]; 89 | 90 | // Set the font's base font 91 | const char *fontName = nil; 92 | if (CGPDFDictionaryGetName(dict, kBaseFontKey, &fontName)) 93 | { 94 | self.baseFont = [NSString stringWithCString:fontName encoding:NSUTF8StringEncoding]; 95 | } 96 | 97 | // NOTE: Any furhter initialization is performed by the appropriate subclass 98 | } 99 | return self; 100 | } 101 | 102 | #pragma mark Font Resources 103 | 104 | - (void)setEncodingWithFontDictionary:(CGPDFDictionaryRef)fontDictionary 105 | { 106 | const char *encodingName = nil; 107 | if (!CGPDFDictionaryGetName(fontDictionary, kEncodingKey, &encodingName)) 108 | { 109 | CGPDFDictionaryRef encodingDict = nil; 110 | CGPDFDictionaryGetDictionary(fontDictionary, kEncodingKey, &encodingDict); 111 | CGPDFDictionaryGetName(encodingDict, kBaseEncodingKey, &encodingName); 112 | 113 | // TODO: Also get differences from font encoding dictionary 114 | } 115 | 116 | [self setEncodingNamed:[NSString stringWithCString:encodingName encoding:NSUTF8StringEncoding]]; 117 | } 118 | 119 | - (void)setEncodingNamed:(NSString *)encodingName 120 | { 121 | if ([@"MacRomanEncoding" isEqualToString:encodingName]) 122 | { 123 | self.encoding = MacRomanEncoding; 124 | } 125 | else if ([@"WinAnsiEncoding" isEqualToString:encodingName]) 126 | { 127 | self.encoding = WinAnsiEncoding; 128 | } 129 | else 130 | { 131 | self.encoding = UnknownEncoding; 132 | } 133 | } 134 | 135 | /* Import font descriptor */ 136 | - (void)setFontDescriptorWithFontDictionary:(CGPDFDictionaryRef)dict 137 | { 138 | CGPDFDictionaryRef descriptor; 139 | if (!CGPDFDictionaryGetDictionary(dict, kFontDescriptorKey, &descriptor)) return; 140 | FontDescriptor *desc = [[FontDescriptor alloc] initWithPDFDictionary:descriptor]; 141 | self.fontDescriptor = desc; 142 | } 143 | 144 | /* Populate the widths array given font dictionary */ 145 | - (void)setWidthsWithFontDictionary:(CGPDFDictionaryRef)dict 146 | { 147 | self.widths = [NSMutableDictionary dictionary]; 148 | } 149 | 150 | /* Parse the ToUnicode map */ 151 | - (void)setToUnicodeWithFontDictionary:(CGPDFDictionaryRef)dict 152 | { 153 | CGPDFStreamRef stream; 154 | if (!CGPDFDictionaryGetStream(dict, kToUnicodeKey, &stream)) return; 155 | CMap *map = [[CMap alloc] initWithPDFStream:stream]; 156 | self.toUnicode = map; 157 | } 158 | 159 | #pragma mark Font Property Accessors 160 | 161 | - (NSString *)unicodeStringUsingFontFile:(const unsigned char *)codes length:(size_t)length 162 | { 163 | FontFile *fontFile = self.fontDescriptor.fontFile; 164 | NSMutableString *unicodeString = [NSMutableString string]; 165 | for (int i = 0; i < length; i++) 166 | { 167 | NSString *string = [fontFile stringWithCode:codes[i]]; 168 | [unicodeString appendString:string]; 169 | } 170 | return unicodeString; 171 | } 172 | 173 | - (unichar)mappedUnicodeValue:(CharacterCode)character 174 | { 175 | unichar value = [self.toUnicode unicodeCharacter:character]; 176 | return (value == NSNotFound) ? value : [self encodedUnicodeValue:character]; 177 | } 178 | 179 | - (unichar)encodedUnicodeValue:(CharacterCode)character 180 | { 181 | NSStringEncoding stringEncoding = nativeEncoding(self.encoding); 182 | const char str[] = {character, '\0'}; 183 | return [[NSString stringWithCString:str encoding:stringEncoding] characterAtIndex:0]; 184 | } 185 | 186 | /*! 187 | Returns a unicode string equivalent to the argument string of character codes. 188 | This method relies on either: 189 | - the font having a known encoding (such as Mac OS Roman), 190 | - a specified standard mapping, 191 | - an embedded Unicode mapping, or 192 | - a mapping embedded inside a font file 193 | 194 | If neither of these produces a Unicode value, the text content can not be extracted. 195 | */ 196 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 197 | { 198 | // Character codes 199 | CharacterCode *characterCodes = CGPDFStringGetBytePtr(pdfString); 200 | NSInteger characterCodeCount = CGPDFStringGetLength(pdfString); 201 | 202 | NSMutableString *string = [NSMutableString string]; 203 | for (int i = 0; i < characterCodeCount; i++) 204 | { 205 | CharacterCode code = characterCodes[i]; 206 | unichar value = [self mappedUnicodeValue:code]; 207 | [string appendFormat:@"%C", value]; 208 | } 209 | 210 | return [NSString stringWithString:string]; 211 | } 212 | 213 | - (NSString *)cidWithPDFString:(CGPDFStringRef)pdfString 214 | { 215 | // Copy PDFString to NSString 216 | NSString *string = (__bridge NSString *) CGPDFStringCopyTextString(pdfString); 217 | return string; 218 | } 219 | 220 | - (NSString *)unicodeWithPDFString:(CGPDFStringRef)pdfString 221 | { 222 | const unsigned char *bytes = CGPDFStringGetBytePtr(pdfString); 223 | NSInteger length = CGPDFStringGetLength(pdfString); 224 | 225 | if (self.toUnicode) 226 | { 227 | NSMutableString *unicodeString = [NSMutableString string]; 228 | 229 | for (int i = 0; i < length; i++) 230 | { 231 | const unsigned char cid = bytes[i]; 232 | [unicodeString appendFormat:@"%C", [self.toUnicode unicodeCharacter:cid]]; 233 | } 234 | return unicodeString; 235 | } 236 | else 237 | { 238 | return [self stringWithPDFString:pdfString]; 239 | } 240 | } 241 | 242 | /* Lowest point of any character */ 243 | - (CGFloat)minY 244 | { 245 | return [self.fontDescriptor descent]; 246 | } 247 | 248 | /* Highest point of any character */ 249 | - (CGFloat)maxY 250 | { 251 | return [self.fontDescriptor ascent]; 252 | } 253 | 254 | /* Width of the given character (CID) scaled to fontsize */ 255 | - (CGFloat)widthOfCharacter:(unichar)character withFontSize:(CGFloat)fontSize 256 | { 257 | NSNumber *key = [NSNumber numberWithInt:character]; 258 | NSNumber *width = [self.widths objectForKey:key]; 259 | return [width floatValue] * fontSize; 260 | } 261 | 262 | /* Ligatures available in the current font encoding */ 263 | - (NSDictionary *)ligatures 264 | { 265 | if (!ligatures) 266 | { 267 | // Mapping ligature Unicode character values to strings 268 | ligatures = [NSDictionary dictionaryWithObjectsAndKeys: 269 | @"ff", [NSString stringWithFormat:@"%C", (unichar) 0xfb00], 270 | @"fi", [NSString stringWithFormat:@"%C", (unichar) 0xfb01], 271 | @"fl", [NSString stringWithFormat:@"%C", (unichar) 0xfb02], 272 | @"ae", [NSString stringWithFormat:@"%C", (unichar) 0x00e6], 273 | @"oe", [NSString stringWithFormat:@"%C", (unichar) 0x0153], nil]; 274 | } 275 | 276 | return ligatures; 277 | } 278 | 279 | /* Width of space chacacter in glyph space */ 280 | - (CGFloat)widthOfSpace 281 | { 282 | return [self widthOfCharacter:0x20 withFontSize:1.0]; 283 | } 284 | 285 | - (NSString *)description 286 | { 287 | NSMutableString *string = [NSMutableString string]; 288 | [string appendFormat:@"%@ {\n", self.baseFont]; 289 | [string appendFormat:@"\ttype = %@\n", [self classForKeyedArchiver]]; 290 | [string appendFormat:@"\tcharacter widths = %d\n", [self.widths count]]; 291 | [string appendFormat:@"\ttoUnicode = %d\n", (self.toUnicode != nil)]; 292 | 293 | if (self.descendantFonts) 294 | { 295 | [string appendFormat:@"\tdescendant fonts = %d\n", [self.descendantFonts count]]; 296 | } 297 | 298 | [string appendFormat:@"}\n"]; 299 | return string; 300 | } 301 | 302 | /* Replace defined ligatures with separate characters */ 303 | - (NSString *)stringByExpandingLigatures:(NSString *)string 304 | { 305 | NSString *replacement = nil; 306 | 307 | for (NSString *ligature in self.ligatures) 308 | { 309 | replacement = [self.ligatures objectForKey:ligature]; 310 | if (!replacement) continue; 311 | string = [string stringByReplacingOccurrencesOfString:ligature withString:replacement]; 312 | } 313 | 314 | return string; 315 | } 316 | 317 | @synthesize fontDescriptor, widths, toUnicode, widthsRange, baseFont, baseFontName, encoding, descendantFonts; 318 | 319 | @end -------------------------------------------------------------------------------- /PDFKitten/FontCollection.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Font.h" 3 | 4 | @interface FontCollection : NSObject { 5 | NSMutableDictionary *fonts; 6 | NSArray *names; 7 | } 8 | 9 | /* Initialize with a font collection dictionary */ 10 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict; 11 | 12 | /* Return the specified font */ 13 | - (Font *)fontNamed:(NSString *)fontName; 14 | 15 | @property (nonatomic, readonly) NSDictionary *fontsByName; 16 | 17 | @property (nonatomic, readonly) NSArray *names; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PDFKitten/FontCollection.m: -------------------------------------------------------------------------------- 1 | #import "FontCollection.h" 2 | 3 | 4 | @implementation FontCollection 5 | 6 | /* Applier function for font dictionaries */ 7 | void didScanFont(const char *key, CGPDFObjectRef object, void *collection) 8 | { 9 | if (!CGPDFObjectGetType(object) == kCGPDFObjectTypeDictionary) return; 10 | CGPDFDictionaryRef dict; 11 | if (!CGPDFObjectGetValue(object, kCGPDFObjectTypeDictionary, &dict)) return; 12 | Font *font = [Font fontWithDictionary:dict]; 13 | if (!font) return; 14 | NSString *name = [NSString stringWithUTF8String:key]; 15 | [(__bridge NSMutableDictionary *)collection setObject:font forKey:name]; 16 | } 17 | 18 | /* Initialize with a font collection dictionary */ 19 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 20 | { 21 | if ((self = [super init])) 22 | { 23 | fonts = [[NSMutableDictionary alloc] init]; 24 | // Enumerate the Font resource dictionary 25 | CGPDFDictionaryApplyFunction(dict, didScanFont, (__bridge void *)(fonts)); 26 | 27 | NSMutableArray *namesArray = [NSMutableArray array]; 28 | 29 | for (NSString *name in [fonts allKeys]) 30 | { 31 | [namesArray addObject:name]; 32 | } 33 | 34 | names = [namesArray sortedArrayUsingSelector:@selector(compare:)]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | /* Returns a copy of the font dictionary */ 41 | - (NSDictionary *)fontsByName 42 | { 43 | return [NSDictionary dictionaryWithDictionary:fonts]; 44 | } 45 | 46 | /* Return the specified font */ 47 | - (Font *)fontNamed:(NSString *)fontName 48 | { 49 | return [fonts objectForKey:fontName]; 50 | } 51 | 52 | @synthesize names; 53 | 54 | @end -------------------------------------------------------------------------------- /PDFKitten/FontDescriptor.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "FontFile.h" 3 | 4 | 5 | 6 | /* Flags as defined in PDF 1.7 */ 7 | typedef enum FontFlags 8 | { 9 | FontFixedPitch = 1 << 0, 10 | FontSerif = 1 << 1, 11 | FontSymbolic = 1 << 2, 12 | FontScript = 1 << 3, 13 | FontNonSymbolic = 1 << 5, 14 | FontItalic = 1 << 6, 15 | FontAllCap = 1 << 16, 16 | FontSmallCap = 1 << 17, 17 | FontForceBold = 1 << 18, 18 | } FontFlags; 19 | 20 | 21 | @interface FontDescriptor : NSObject { 22 | CGFloat descent; 23 | CGFloat ascent; 24 | CGFloat leading; 25 | CGFloat capHeight; 26 | CGFloat xHeight; 27 | CGFloat averageWidth; 28 | CGFloat maxWidth; 29 | CGFloat missingWidth; 30 | CGFloat verticalStemWidth; 31 | CGFloat horizontalStemHeigth; 32 | CGFloat italicAngle; 33 | CGRect bounds; 34 | NSUInteger flags; 35 | NSString *fontName; 36 | FontFile *fontFile; 37 | } 38 | 39 | /* Initialize a descriptor using a FontDescriptor dictionary */ 40 | - (id)initWithPDFDictionary:(CGPDFDictionaryRef)dict; 41 | 42 | // TODO: temporarily public 43 | + (void)parseFontFile:(NSData *)data; 44 | 45 | @property (nonatomic, assign) CGRect bounds; 46 | @property (nonatomic, assign) CGFloat ascent; 47 | @property (nonatomic, assign) CGFloat descent; 48 | @property (nonatomic, assign) CGFloat leading; 49 | @property (nonatomic, assign) CGFloat capHeight; 50 | @property (nonatomic, assign) CGFloat xHeight; 51 | @property (nonatomic, assign) CGFloat averageWidth; 52 | @property (nonatomic, assign) CGFloat maxWidth; 53 | @property (nonatomic, assign) CGFloat missingWidth; 54 | @property (nonatomic, assign) CGFloat verticalStemWidth; 55 | @property (nonatomic, assign) CGFloat horizontalStemWidth; 56 | @property (nonatomic, assign) CGFloat italicAngle; 57 | @property (nonatomic, assign) NSUInteger flags; 58 | @property (nonatomic, readonly, getter = isSymbolic) BOOL symbolic; 59 | @property (nonatomic, copy) NSString *fontName; 60 | @property (nonatomic, readonly) FontFile *fontFile; 61 | @end 62 | -------------------------------------------------------------------------------- /PDFKitten/FontDescriptor.m: -------------------------------------------------------------------------------- 1 | #import "FontDescriptor.h" 2 | #import "TrueTypeFont.h" 3 | #import 4 | 5 | const char *kAscentKey = "Ascent"; 6 | const char *kDescentKey = "Descent"; 7 | const char *kLeadingKey = "Leading"; 8 | const char *kCapHeightKey = "CapHeight"; 9 | const char *kXHeightKey = "XHeight"; 10 | const char *kAverageWidthKey = "AvgWidth"; 11 | const char *kMaxWidthKey = "MaxWidth"; 12 | const char *kMissingWidthKey = "MissingWidth"; 13 | const char *kFlagsKey = "Flags"; 14 | const char *kStemVKey = "StemV"; 15 | const char *kStemHKey = "StemH"; 16 | const char *kItalicAngleKey = "ItalicAngle"; 17 | const char *kFontNameKey = "FontName"; 18 | const char *kFontBBoxKey = "FontBBox"; 19 | const char *kFontFileKey = "FontFile"; 20 | 21 | 22 | @implementation FontDescriptor 23 | 24 | - (id)initWithPDFDictionary:(CGPDFDictionaryRef)dict 25 | { 26 | const char *type = nil; 27 | CGPDFDictionaryGetName(dict, kTypeKey, &type); 28 | 29 | if (!type || strcmp(type, kFontDescriptorKey) != 0) 30 | { 31 | return nil; 32 | } 33 | 34 | if ((self = [super init])) 35 | { 36 | CGPDFInteger ascentValue = 0L; 37 | CGPDFInteger descentValue = 0L; 38 | CGPDFInteger leadingValue = 0L; 39 | CGPDFInteger capHeightValue = 0L; 40 | CGPDFInteger xHeightValue = 0L; 41 | CGPDFInteger averageWidthValue = 0L; 42 | CGPDFInteger maxWidthValue = 0L; 43 | CGPDFInteger missingWidthValue = 0L; 44 | CGPDFInteger flagsValue = 0L; 45 | CGPDFInteger stemV = 0L; 46 | CGPDFInteger stemH = 0L; 47 | CGPDFInteger italicAngleValue = 0L; 48 | const char *fontNameString = nil; 49 | CGPDFArrayRef bboxValue = nil; 50 | 51 | CGPDFDictionaryGetInteger(dict, kAscentKey, &ascentValue); 52 | CGPDFDictionaryGetInteger(dict, kDescentKey, &descentValue); 53 | CGPDFDictionaryGetInteger(dict, kLeadingKey, &leadingValue); 54 | CGPDFDictionaryGetInteger(dict, kCapHeightKey, &capHeightValue); 55 | CGPDFDictionaryGetInteger(dict, kXHeightKey, &xHeightValue); 56 | CGPDFDictionaryGetInteger(dict, kAverageWidthKey, &averageWidthValue); 57 | CGPDFDictionaryGetInteger(dict, kMaxWidthKey, &maxWidthValue); 58 | CGPDFDictionaryGetInteger(dict, kMissingWidthKey, &missingWidthValue); 59 | CGPDFDictionaryGetInteger(dict, kFlagsKey, &flagsValue); 60 | CGPDFDictionaryGetInteger(dict, kStemVKey, &stemV); 61 | CGPDFDictionaryGetInteger(dict, kStemHKey, &stemH); 62 | CGPDFDictionaryGetInteger(dict, kItalicAngleKey, &italicAngleValue); 63 | CGPDFDictionaryGetName(dict, kFontNameKey, &fontNameString); 64 | CGPDFDictionaryGetArray(dict, kFontBBoxKey, &bboxValue); 65 | 66 | self.ascent = ascentValue; 67 | self.descent = descentValue; 68 | self.leading = leadingValue; 69 | self.capHeight = capHeightValue; 70 | self.xHeight = xHeightValue; 71 | self.averageWidth = averageWidthValue; 72 | self.maxWidth = maxWidthValue; 73 | self.missingWidth = missingWidthValue; 74 | self.flags = flagsValue; 75 | self.verticalStemWidth = stemV; 76 | self.horizontalStemWidth = stemH; 77 | self.italicAngle = italicAngleValue; 78 | self.fontName = [NSString stringWithUTF8String:fontNameString]; 79 | 80 | if (CGPDFArrayGetCount(bboxValue) == 4) 81 | { 82 | CGPDFInteger x = 0, y = 0, width = 0, height = 0; 83 | CGPDFArrayGetInteger(bboxValue, 0, &x); 84 | CGPDFArrayGetInteger(bboxValue, 1, &y); 85 | CGPDFArrayGetInteger(bboxValue, 2, &width); 86 | CGPDFArrayGetInteger(bboxValue, 3, &height); 87 | self.bounds = CGRectMake(x, y, width, height); 88 | } 89 | 90 | CGPDFStreamRef fontFileStream; 91 | 92 | if (CGPDFDictionaryGetStream(dict, kFontFileKey, &fontFileStream)) 93 | { 94 | CGPDFDataFormat format; 95 | NSData *data = (__bridge NSData *) CGPDFStreamCopyData(fontFileStream, &format); 96 | fontFile = [[FontFile alloc] initWithData:data]; 97 | } 98 | } 99 | 100 | return self; 101 | } 102 | 103 | /* True if a font is symbolic */ 104 | - (BOOL)isSymbolic 105 | { 106 | return ((self.flags & FontSymbolic) > 0) && ((self.flags & FontNonSymbolic) == 0); 107 | } 108 | 109 | @synthesize ascent, descent, bounds, leading, capHeight, averageWidth, maxWidth, missingWidth, xHeight, flags, verticalStemWidth, horizontalStemWidth, italicAngle, fontName; 110 | @synthesize fontFile; 111 | 112 | @end -------------------------------------------------------------------------------- /PDFKitten/FontFile.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FontFile : NSObject { 4 | NSData *data; 5 | NSString *text; 6 | size_t asciiTextLength; 7 | NSMutableDictionary *names; 8 | } 9 | 10 | + (unichar)characterByName:(NSString *)name; 11 | 12 | - (id)initWithContentsOfURL:(NSURL *)url; 13 | 14 | - (id)initWithData:(NSData *)data; 15 | 16 | - (NSString *)stringWithCode:(int)code; 17 | 18 | @property (nonatomic, readonly) NSString *text; 19 | @property (nonatomic, readonly) NSData *data; 20 | @property (nonatomic, readonly) NSDictionary *names; 21 | @end 22 | -------------------------------------------------------------------------------- /PDFKitten/FontFile.m: -------------------------------------------------------------------------------- 1 | #import "FontFile.h" 2 | 3 | #define kHeaderLength 6 4 | 5 | static NSDictionary *charactersByName = nil; 6 | 7 | @implementation FontFile 8 | 9 | - (id)initWithContentsOfURL:(NSURL *)url 10 | { 11 | return [self initWithData:[NSData dataWithContentsOfURL:url]]; 12 | } 13 | 14 | - (id)initWithData:(NSData *)someData 15 | { 16 | if ((self = [super init])) 17 | { 18 | if (!someData) 19 | { 20 | return nil; 21 | } 22 | 23 | data = someData; 24 | NSScanner *scanner = [NSScanner scannerWithString:self.text]; 25 | NSCharacterSet *delimiterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 26 | NSCharacterSet *newlineCharacterSet = [NSCharacterSet newlineCharacterSet]; 27 | 28 | names = [NSMutableDictionary dictionary]; 29 | NSString *buffer; 30 | 31 | while (![scanner isAtEnd]) 32 | { 33 | if (![scanner scanUpToCharactersFromSet:delimiterSet intoString:&buffer]) 34 | { 35 | break; 36 | } 37 | 38 | if ([buffer hasPrefix:@"%"]) 39 | { 40 | [scanner scanUpToCharactersFromSet:newlineCharacterSet intoString:nil]; 41 | continue; 42 | } 43 | 44 | if ([buffer isEqualToString:@"dup"]) 45 | { 46 | int code; 47 | NSString *name; 48 | [scanner scanInt:&code]; 49 | [scanner scanUpToCharactersFromSet:delimiterSet intoString:&name]; 50 | 51 | if (name) 52 | { 53 | [names setObject:name forKey:[NSNumber numberWithInt:code]]; 54 | } 55 | } 56 | } 57 | } 58 | return self; 59 | } 60 | 61 | + (unichar)characterByName:(NSString *)name 62 | { 63 | if (!charactersByName) 64 | { 65 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 66 | 67 | 68 | [NSNumber numberWithInt:0xfb00], @"/ff", 69 | [NSNumber numberWithInt:0xfb01], @"/fi", 70 | [NSNumber numberWithInt:0xfb02], @"/fl", 71 | [NSNumber numberWithInt:0xfb04], @"/ffl", 72 | 73 | [NSNumber numberWithInt:0x0054], @"/T", 74 | [NSNumber numberWithInt:0x0061], @"/a", 75 | [NSNumber numberWithInt:0x0063], @"/c", 76 | [NSNumber numberWithInt:0x0065], @"/e", 77 | [NSNumber numberWithInt:0x0068], @"/h", 78 | [NSNumber numberWithInt:0x0069], @"/i", 79 | [NSNumber numberWithInt:0x006c], @"/l", 80 | [NSNumber numberWithInt:0x006e], @"/n", 81 | [NSNumber numberWithInt:0x006f], @"/o", 82 | [NSNumber numberWithInt:0x0031], @"/one", 83 | [NSNumber numberWithInt:0x002e], @"/period", 84 | [NSNumber numberWithInt:0x0073], @"/s", 85 | [NSNumber numberWithInt:0x0074], @"/t", 86 | [NSNumber numberWithInt:0x0075], @"/u", 87 | [NSNumber numberWithInt:0x0076], @"/v", 88 | [NSNumber numberWithInt:0x0079], @"/y", 89 | nil]; 90 | 91 | charactersByName = dict; 92 | } 93 | 94 | return [[charactersByName objectForKey:name] intValue]; 95 | } 96 | 97 | - (NSString *)stringWithCode:(int)code 98 | { 99 | static NSString *singleUnicodeCharFormat = @"%C"; 100 | NSString *characterName = [names objectForKey:[NSNumber numberWithInt:code]]; 101 | unichar unicodeValue = [FontFile characterByName:characterName]; 102 | if (!unicodeValue) unicodeValue = code; 103 | return [NSString stringWithFormat:singleUnicodeCharFormat, unicodeValue]; 104 | } 105 | 106 | - (NSString *)text 107 | { 108 | if (!text) 109 | { 110 | // ASCII segment length (little endian) 111 | unsigned char *bytes = (uint8_t *) [self.data bytes]; 112 | if (bytes[0] == 0x80) 113 | { 114 | asciiTextLength = bytes[2] | bytes[3] << 8 | bytes[4] << 16 | bytes[5] << 24; 115 | NSData *textData = [[NSData alloc] initWithBytes:bytes+kHeaderLength length:asciiTextLength]; 116 | text = [[NSString alloc] initWithData:textData encoding:NSASCIIStringEncoding]; 117 | } 118 | else 119 | { 120 | text = [[NSString alloc] initWithData:self.data encoding:NSASCIIStringEncoding]; 121 | } 122 | } 123 | return text; 124 | } 125 | 126 | @synthesize data, text, names; 127 | 128 | @end -------------------------------------------------------------------------------- /PDFKitten/MMType1Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SimpleFont.h" 3 | 4 | @interface MMType1Font : SimpleFont { 5 | 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/MMType1Font.m: -------------------------------------------------------------------------------- 1 | #import "MMType1Font.h" 2 | 3 | 4 | @implementation MMType1Font 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /PDFKitten/PDFKitten-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.KurtCode.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | NSMainNibFile 30 | MainWindow 31 | UISupportedInterfaceOrientations~ipad 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /PDFKitten/PDFKitten-Prefix.pch: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #ifndef __IPHONE_3_0 4 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 5 | #endif 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /PDFKitten/PDFKittenAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "RootViewController.h" 3 | 4 | @interface PDFKittenAppDelegate : NSObject { 5 | IBOutlet RootViewController *rootViewController; 6 | } 7 | 8 | @property (nonatomic, retain) IBOutlet UIWindow *window; 9 | @end 10 | -------------------------------------------------------------------------------- /PDFKitten/PDFKittenAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "PDFKittenAppDelegate.h" 2 | 3 | @implementation PDFKittenAppDelegate 4 | 5 | 6 | @synthesize window=_window; 7 | 8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 9 | { 10 | self.window.rootViewController = rootViewController; 11 | [self.window makeKeyAndVisible]; 12 | return YES; 13 | } 14 | 15 | - (void)applicationWillResignActive:(UIApplication *)application 16 | { 17 | /* 18 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 19 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 20 | */ 21 | } 22 | 23 | - (void)applicationDidEnterBackground:(UIApplication *)application 24 | { 25 | /* 26 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 27 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 28 | */ 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | /* 34 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | */ 36 | } 37 | 38 | - (void)applicationDidBecomeActive:(UIApplication *)application 39 | { 40 | /* 41 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | */ 43 | } 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application 46 | { 47 | /* 48 | Called when the application is about to terminate. 49 | Save data if appropriate. 50 | See also applicationDidEnterBackground:. 51 | */ 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /PDFKitten/PDFPage.h: -------------------------------------------------------------------------------- 1 | #import "Page.h" 2 | #import "Scanner.h" 3 | 4 | 5 | @interface PDFContentView : PageContentView { 6 | CGPDFPageRef pdfPage; 7 | NSString *keyword; 8 | NSArray *selections; 9 | Scanner *scanner; 10 | } 11 | 12 | #pragma mark 13 | 14 | - (void)setPage:(CGPDFPageRef)page; 15 | 16 | @property (nonatomic, retain) Scanner *scanner; 17 | @property (nonatomic, copy) NSString *keyword; 18 | @property (nonatomic, copy) NSArray *selections; 19 | 20 | @end 21 | 22 | #pragma mark 23 | 24 | @interface PDFPage : Page { 25 | } 26 | 27 | #pragma mark 28 | 29 | - (void)setPage:(CGPDFPageRef)page; 30 | 31 | @property (nonatomic, copy) NSString *keyword; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /PDFKitten/PDFPage.m: -------------------------------------------------------------------------------- 1 | #import "PDFPage.h" 2 | #import 3 | 4 | @implementation PDFContentView 5 | 6 | #pragma mark - Initialization 7 | 8 | - (id)initWithFrame:(CGRect)frame 9 | { 10 | if ((self = [super initWithFrame:frame])) 11 | { 12 | self.backgroundColor = [UIColor whiteColor]; 13 | 14 | CATiledLayer *tiledLayer = (CATiledLayer *) [self layer]; 15 | tiledLayer.frame = CGRectMake(0, 0, 100, 100); 16 | [tiledLayer setTileSize:CGSizeMake(1024, 1024)]; 17 | [tiledLayer setLevelsOfDetail:4]; 18 | [tiledLayer setLevelsOfDetailBias:4]; 19 | } 20 | return self; 21 | } 22 | 23 | + (Class)layerClass 24 | { 25 | return [CATiledLayer class]; 26 | } 27 | 28 | - (void)setKeyword:(NSString *)str 29 | { 30 | keyword = str; 31 | self.selections = nil; 32 | } 33 | 34 | - (NSArray *)selections 35 | { 36 | @synchronized (self) 37 | { 38 | if (!selections) 39 | { 40 | self.selections = [self.scanner select:self.keyword]; 41 | } 42 | return selections; 43 | } 44 | } 45 | 46 | - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx 47 | { 48 | CGContextSetFillColorWithColor(ctx, [[UIColor whiteColor] CGColor]); 49 | CGContextFillRect(ctx, layer.bounds); 50 | 51 | // Flip the coordinate system 52 | CGContextTranslateCTM(ctx, 0.0, layer.bounds.size.height); 53 | CGContextScaleCTM(ctx, 1.0, -1.0); 54 | 55 | // Transform coordinate system to match PDF 56 | NSInteger rotationAngle = CGPDFPageGetRotationAngle(pdfPage); 57 | CGAffineTransform transform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFCropBox, layer.bounds, -rotationAngle, YES); 58 | CGContextConcatCTM(ctx, transform); 59 | 60 | CGContextDrawPDFPage(ctx, pdfPage); 61 | 62 | if (self.keyword) 63 | { 64 | CGContextSetFillColorWithColor(ctx, [[UIColor yellowColor] CGColor]); 65 | CGContextSetBlendMode(ctx, kCGBlendModeMultiply); 66 | for (Selection *s in self.selections) 67 | { 68 | CGContextSaveGState(ctx); 69 | CGContextConcatCTM(ctx, s.transform); 70 | CGContextFillRect(ctx, s.frame); 71 | CGContextRestoreGState(ctx); 72 | } 73 | } 74 | } 75 | 76 | #pragma mark PDF drawing 77 | 78 | /* Draw the PDFPage to the content view */ 79 | - (void)drawRect:(CGRect)rect 80 | { 81 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 82 | 83 | CGContextSetFillColorWithColor(ctx, [[UIColor redColor] CGColor]); 84 | CGContextFillRect(ctx, rect); 85 | } 86 | 87 | /* Sets the current PDFPage object */ 88 | - (void)setPage:(CGPDFPageRef)page 89 | { 90 | CGPDFPageRelease(pdfPage); 91 | pdfPage = CGPDFPageRetain(page); 92 | self.scanner = [Scanner scannerWithPage:pdfPage]; 93 | } 94 | 95 | - (void)dealloc 96 | { 97 | CGPDFPageRelease(pdfPage); 98 | } 99 | 100 | @synthesize keyword, selections, scanner; 101 | @end 102 | 103 | #pragma mark - 104 | 105 | @implementation PDFPage 106 | 107 | #pragma mark - 108 | 109 | - (void)setNeedsDisplay 110 | { 111 | [super setNeedsDisplay]; 112 | [contentView setNeedsDisplay]; 113 | } 114 | 115 | /* Override implementation to return a PDFContentView */ 116 | - (UIView *)contentView 117 | { 118 | if (!contentView) 119 | { 120 | contentView = [[PDFContentView alloc] initWithFrame:CGRectZero]; 121 | } 122 | return contentView; 123 | } 124 | 125 | - (void)setPage:(CGPDFPageRef)page 126 | { 127 | [(PDFContentView *)self.contentView setPage:page]; 128 | // Also set the frame of the content view according to the page size 129 | CGRect rect = CGPDFPageGetBoxRect(page, kCGPDFCropBox); 130 | self.contentView.frame = rect; 131 | } 132 | 133 | - (void)setKeyword:(NSString *)string 134 | { 135 | ((PDFContentView *)contentView).keyword = string; 136 | } 137 | 138 | - (NSString *)keyword 139 | { 140 | return ((PDFContentView *)contentView).keyword; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /PDFKitten/PDFPageDetailsView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "FontCollection.h" 3 | 4 | @interface PDFPageDetailsView : UINavigationController { 5 | FontCollection *fontCollection; 6 | } 7 | 8 | - (id)initWithFont:(FontCollection *)fontCollection; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /PDFKitten/PDFPageDetailsView.m: -------------------------------------------------------------------------------- 1 | #import "PDFPageDetailsView.h" 2 | 3 | @implementation PDFPageDetailsView 4 | 5 | - (id)initWithFont:(FontCollection *)aFontCollection 6 | { 7 | fontCollection = aFontCollection; 8 | UITableViewController *rvc = [[UITableViewController alloc] initWithStyle:UITableViewStyleGrouped]; 9 | rvc.tableView.delegate = self; 10 | rvc.tableView.dataSource = self; 11 | self = [super initWithRootViewController:rvc]; 12 | self.navigationBarHidden = YES; 13 | 14 | return self; 15 | } 16 | 17 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 18 | { 19 | return [[fontCollection fontsByName] count]; 20 | } 21 | 22 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 23 | { 24 | return 3; 25 | } 26 | 27 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 28 | { 29 | return [[fontCollection names] objectAtIndex:section]; 30 | } 31 | 32 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 33 | { 34 | static NSString *identifier = @"CellIdentifier"; 35 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 36 | 37 | if (!cell) 38 | { 39 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier]; 40 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 41 | } 42 | 43 | 44 | NSString *name = [[fontCollection names] objectAtIndex:indexPath.section]; 45 | Font *font = [fontCollection fontNamed:name]; 46 | cell.accessoryType = UITableViewCellAccessoryNone; 47 | 48 | switch (indexPath.row) 49 | { 50 | case 0: 51 | cell.textLabel.text = @"Type"; 52 | cell.detailTextLabel.text = [NSString stringWithFormat:@"%@", [font class]]; 53 | break; 54 | case 1: 55 | { 56 | NSRange range = font.widthsRange; 57 | cell.textLabel.text = @"Widths"; 58 | cell.detailTextLabel.text = [NSString stringWithFormat:@"%d (%d - %d)", [[font widths] count], range.location, NSMaxRange(range)]; 59 | cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; 60 | break; 61 | } 62 | case 2: 63 | cell.textLabel.text = @"Flags"; 64 | cell.detailTextLabel.text = [NSString stringWithFormat:@"%d",[[font fontDescriptor] flags]]; 65 | break; 66 | default: 67 | break; 68 | } 69 | 70 | return cell; 71 | } 72 | 73 | @end -------------------------------------------------------------------------------- /PDFKitten/Page.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface PageContentView : UIView { 5 | } 6 | 7 | @end 8 | 9 | @interface Page : UIScrollView { 10 | NSInteger pageNumber; 11 | UIView *contentView; 12 | UIView *detailedView; 13 | } 14 | - (UIView *)contentView; 15 | 16 | @property (nonatomic, retain) UIView *contentView; 17 | @property (nonatomic, assign) NSInteger pageNumber; 18 | @property (nonatomic, retain) UIView *detailedView; 19 | @end 20 | -------------------------------------------------------------------------------- /PDFKitten/Page.m: -------------------------------------------------------------------------------- 1 | #import "Page.h" 2 | 3 | 4 | @implementation PageContentView 5 | 6 | @end 7 | 8 | @implementation Page 9 | 10 | - (id)initWithFrame:(CGRect)frame 11 | { 12 | if ((self = [super initWithFrame:frame])) 13 | { 14 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 15 | 16 | UIView *view = [self contentView]; 17 | view.frame = frame; 18 | self.contentView = view; 19 | [self addSubview:view]; 20 | 21 | self.maximumZoomScale = 5.0; 22 | 23 | self.delegate = self; 24 | 25 | UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didDoubleTap:)]; 26 | doubleTap.numberOfTapsRequired = 2; 27 | [self addGestureRecognizer:doubleTap]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)presentDetailedView:(UIView *)view 33 | { 34 | 35 | } 36 | 37 | /* Double tap zooms the content to fill the container view */ 38 | - (void)didDoubleTap:(UITapGestureRecognizer *)recognizer 39 | { 40 | CGFloat hScale = CGRectGetWidth(self.bounds) / CGRectGetWidth(contentView.bounds); 41 | CGFloat vScale = CGRectGetHeight(self.bounds) / CGRectGetHeight(contentView.bounds); 42 | [self setZoomScale:MIN(hScale, vScale) animated:YES]; 43 | } 44 | 45 | - (UIView *)contentView 46 | { 47 | if (!contentView) 48 | { 49 | contentView = [[UIView alloc] initWithFrame:CGRectZero]; 50 | } 51 | return contentView; 52 | } 53 | 54 | #pragma mark - UIScrollView delegate 55 | 56 | /* Zoom the content view when user pinches the scroll view */ 57 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView 58 | { 59 | return contentView; 60 | } 61 | 62 | /* Make the content view center on screen when zoomed out */ 63 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView 64 | { 65 | CGRect frame = self.contentView.frame; 66 | frame.origin = CGPointZero; 67 | // Calculate how much of the content view is outside the screen 68 | CGSize totalInset = CGSizeMake(CGRectGetWidth(contentView.frame) - CGRectGetWidth(self.bounds), 69 | CGRectGetHeight(contentView.frame) - CGRectGetHeight(self.bounds)); 70 | if (totalInset.width < 0) 71 | { 72 | frame.origin.x = - totalInset.width / 2; 73 | } 74 | if (totalInset.height < 0) 75 | { 76 | frame.origin.y = - totalInset.height / 2; 77 | } 78 | self.contentView.frame = frame; 79 | } 80 | 81 | - (void)layoutSubviews 82 | { 83 | UIView *currentContentView = (self.detailedView) ? self.detailedView : self.contentView; 84 | // Set minimum zoom scale to where the content fits the screen 85 | CGFloat hScale = CGRectGetWidth(self.frame) / CGRectGetWidth(currentContentView.bounds); 86 | CGFloat vScale = CGRectGetHeight(self.frame) / CGRectGetHeight(currentContentView.bounds); 87 | [self setMinimumZoomScale:MIN(hScale, vScale)]; 88 | } 89 | 90 | @synthesize pageNumber, contentView, detailedView; 91 | 92 | @end -------------------------------------------------------------------------------- /PDFKitten/PageView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Page.h" 3 | 4 | @class PageView; 5 | @class PDFPageDetailsView; 6 | 7 | @protocol PageViewDelegate 8 | 9 | #pragma mark - Required 10 | 11 | /* Asks the delegate how many pages are in the document */ 12 | - (NSInteger)numberOfPagesInPageView:(PageView *)pageView; 13 | 14 | /* Asks the delegate for a page view object */ 15 | - (Page *)pageView:(PageView *)pageView viewForPage:(NSInteger)page; 16 | 17 | #pragma mark Optional 18 | 19 | @optional 20 | 21 | /* Tells the delegate when the document stopped scrolling at a page */ 22 | - (void)pageView:(PageView *)pageView didScrollToPage:(NSInteger)pageNumber; 23 | 24 | /* Asks the delegate for a keyword */ 25 | - (NSString *)keywordForPageView:(PageView *)pageView; 26 | 27 | /* Detailed view for page */ 28 | - (PDFPageDetailsView *)pageView:(PageView *)pageView detailedViewForPage:(NSInteger)page; 29 | 30 | @end 31 | 32 | #pragma mark 33 | 34 | @interface PageView : UIScrollView { 35 | NSInteger numberOfPages; 36 | NSInteger pageNumber; 37 | NSMutableSet *visiblePages; 38 | NSMutableSet *recycledPages; 39 | NSString *keyword; 40 | PDFPageDetailsView *detailedViewController; 41 | __weak IBOutlet id dataSource; 42 | } 43 | 44 | #pragma mark - 45 | 46 | /* Called when user has pressed the info button */ 47 | - (IBAction)detailedInfoButtonPressed:(id)sender; 48 | 49 | /* Returns a recycled page, or nil if none exist */ 50 | - (Page *)dequeueRecycledPage; 51 | 52 | /* Causes the page view to reload pages */ 53 | - (void)reloadData; 54 | 55 | /* Scroll to a specific page */ 56 | - (void)setPage:(NSInteger)aPage animated:(BOOL)animated; 57 | 58 | /* Page at the given index */ 59 | - (Page *)pageAtIndex:(NSInteger)index; 60 | 61 | /* The page currently visible */ 62 | @property (nonatomic, assign) NSInteger page; 63 | 64 | /* Data source for pages */ 65 | @property (nonatomic, weak) id dataSource; 66 | 67 | @property (nonatomic, retain) NSString *keyword; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /PDFKitten/PageView.m: -------------------------------------------------------------------------------- 1 | #import "PageView.h" 2 | #import "PDFPage.h" 3 | #import "PDFPageDetailsView.h" 4 | 5 | @interface PageView () 6 | @property (nonatomic, retain) PDFPageDetailsView *detailViewController; 7 | @end 8 | 9 | @implementation PageView 10 | 11 | #pragma mark - View layout 12 | 13 | - (id)initWithCoder:(NSCoder *)aDecoder 14 | { 15 | if ((self = [super initWithCoder:aDecoder])) 16 | { 17 | self.delegate = self; 18 | self.pagingEnabled = YES; 19 | recycledPages = [[NSMutableSet alloc] init]; 20 | visiblePages = [[NSMutableSet alloc] init]; 21 | 22 | self.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; 23 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)reloadData 29 | { 30 | for (Page *p in visiblePages) 31 | { 32 | [p removeFromSuperview]; 33 | } 34 | [recycledPages unionSet:visiblePages]; 35 | [visiblePages removeAllObjects]; 36 | [self setNeedsLayout]; 37 | } 38 | 39 | /* True if the page with given index is showing */ 40 | - (BOOL)isShowingPageForIndex:(NSInteger)index 41 | { 42 | for (Page *p in visiblePages) 43 | { 44 | if (p.pageNumber == index) 45 | { 46 | return YES; 47 | } 48 | } 49 | return NO; 50 | } 51 | 52 | - (void)layoutSubviews 53 | { 54 | numberOfPages = [dataSource numberOfPagesInPageView:self]; 55 | 56 | self.contentSize = CGSizeMake(CGRectGetWidth(self.bounds) * numberOfPages, CGRectGetWidth(self.bounds)); 57 | 58 | CGRect visibleBounds = self.bounds; 59 | int firstNeededPageIndex = floorf(CGRectGetMinX(visibleBounds) / CGRectGetWidth(visibleBounds)); 60 | int lastNeededPageIndex = floorf((CGRectGetMaxX(visibleBounds)-1) / CGRectGetWidth(visibleBounds)); 61 | firstNeededPageIndex = MAX(firstNeededPageIndex, 0); 62 | lastNeededPageIndex = MIN(numberOfPages-1, lastNeededPageIndex); 63 | 64 | for (Page *aPage in visiblePages) 65 | { 66 | if (aPage.pageNumber < firstNeededPageIndex || aPage.pageNumber > lastNeededPageIndex) 67 | { 68 | [recycledPages addObject:aPage]; 69 | [aPage removeFromSuperview]; 70 | } 71 | } 72 | [visiblePages minusSet:recycledPages]; 73 | 74 | for (int i = firstNeededPageIndex; i <= lastNeededPageIndex; i++) 75 | { 76 | if ([self isShowingPageForIndex:i]) continue; 77 | 78 | Page *aPage = [dataSource pageView:self viewForPage:i]; 79 | CGRect rect = self.frame; 80 | rect.origin.y = 0; 81 | rect.origin.x = CGRectGetWidth(rect) * i; 82 | aPage.frame = rect; 83 | 84 | [visiblePages addObject:aPage]; 85 | [aPage setNeedsDisplay]; 86 | 87 | [self addSubview:aPage]; 88 | } 89 | } 90 | 91 | - (UIView *)dequeueRecycledPage 92 | { 93 | @synchronized (self) 94 | { 95 | UIView *p = [recycledPages anyObject]; 96 | 97 | if (p) 98 | { 99 | [recycledPages removeObject:p]; 100 | } 101 | 102 | return p; 103 | } 104 | } 105 | 106 | - (Page *)pageAtIndex:(NSInteger)index 107 | { 108 | NSSet *pages = [visiblePages copy]; 109 | 110 | for (Page *p in pages) 111 | { 112 | if (p.pageNumber == index) 113 | { 114 | return p; 115 | } 116 | } 117 | 118 | return nil; 119 | } 120 | 121 | #pragma mark - UIScrollViewDelegate 122 | 123 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 124 | { 125 | [self setNeedsLayout]; 126 | } 127 | 128 | /* Animated scrolling did stop */ 129 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 130 | { 131 | if ([dataSource respondsToSelector:@selector(pageView:didScrollToPage:)]) 132 | { 133 | [dataSource pageView:self didScrollToPage:self.page]; 134 | } 135 | } 136 | 137 | /* User touch scrolling did stop */ 138 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 139 | { 140 | if ([dataSource respondsToSelector:@selector(pageView:didScrollToPage:)]) 141 | { 142 | [dataSource pageView:self didScrollToPage:self.page]; 143 | } 144 | } 145 | 146 | 147 | #pragma mark - Page numbers 148 | 149 | /* Scrolls to the given page */ 150 | - (void)setPage:(NSInteger)aPage animated:(BOOL)animated 151 | { 152 | CGRect rect = self.frame; 153 | rect.origin.x = CGRectGetWidth(self.frame) * aPage; 154 | [self scrollRectToVisible:rect animated:YES]; 155 | if (!animated) 156 | { 157 | if ([dataSource respondsToSelector:@selector(pageView:didScrollToPage:)]) 158 | { 159 | [dataSource pageView:self didScrollToPage:self.page]; 160 | } 161 | } 162 | } 163 | 164 | /* Scrolls to the given page */ 165 | - (void)setPage:(NSInteger)aPage 166 | { 167 | [self setPage:aPage animated:YES]; 168 | } 169 | 170 | /* Returns the current page number */ 171 | - (NSInteger)page 172 | { 173 | CGFloat minimumVisibleX = CGRectGetMinX(self.bounds); 174 | return floorf(minimumVisibleX / CGRectGetWidth(self.frame)); 175 | } 176 | 177 | - (void)setKeyword:(NSString *)str 178 | { 179 | keyword = str; 180 | 181 | for (PDFPage *p in visiblePages) 182 | { 183 | p.keyword = str; 184 | [p setNeedsDisplay]; 185 | } 186 | } 187 | 188 | /* Show detailed view when info button has been pressed */ 189 | - (void)detailedInfoButtonPressed:(UIButton *)sender 190 | { 191 | if (![dataSource respondsToSelector:@selector(pageView:detailedViewForPage:)]) 192 | { 193 | return; 194 | } 195 | 196 | self.detailViewController = [dataSource pageView:self detailedViewForPage:self.page]; 197 | UIView *detailedView = [self.detailViewController view]; 198 | 199 | Page *currentPage = nil; 200 | 201 | for (Page *p in visiblePages) 202 | { 203 | if (p.pageNumber == self.page) 204 | { 205 | currentPage = p; 206 | break; 207 | } 208 | } 209 | 210 | if (!currentPage) return; 211 | 212 | if (currentPage.detailedView) 213 | { 214 | [UIView transitionFromView:currentPage.detailedView toView:currentPage duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil]; 215 | currentPage.detailedView = nil; 216 | return; 217 | } 218 | 219 | currentPage.detailedView = detailedView; 220 | detailedView.frame = currentPage.frame; 221 | [UIView transitionFromView:currentPage toView:detailedView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil]; 222 | } 223 | 224 | @synthesize page, dataSource, keyword, detailViewController; 225 | 226 | @end -------------------------------------------------------------------------------- /PDFKitten/PageViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "PageView.h" 3 | 4 | @interface PageViewController : UIViewController { 5 | IBOutlet PageView *pageView; 6 | } 7 | 8 | @property (nonatomic, readonly) PageView *pageView; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /PDFKitten/PageViewController.m: -------------------------------------------------------------------------------- 1 | #import "PageViewController.h" 2 | 3 | @implementation PageViewController 4 | 5 | - (NSInteger)numberOfPagesInPageView:(PageView *)pageView 6 | { 7 | return 0; 8 | } 9 | 10 | - (Page *)pageView:(PageView *)pageView viewForPage:(NSInteger)page 11 | { 12 | return nil; 13 | } 14 | 15 | 16 | #pragma mark - View lifecycle 17 | 18 | - (void)viewDidAppear:(BOOL)animated 19 | { 20 | [super viewDidAppear:animated]; 21 | [pageView reloadData]; 22 | } 23 | 24 | - (void)viewDidLoad 25 | { 26 | [super viewDidLoad]; 27 | self.pageView.dataSource = self; 28 | } 29 | 30 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 31 | { 32 | return YES; 33 | } 34 | 35 | @synthesize pageView; 36 | @end 37 | -------------------------------------------------------------------------------- /PDFKitten/PageViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11A511 6 | 1910 7 | 1138 8 | 566.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 911 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIScrollView 17 | 18 | 19 | YES 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | PluginDependencyRecalculationVersion 24 | 25 | 26 | 27 | YES 28 | 29 | IBFilesOwner 30 | IBIPadFramework 31 | 32 | 33 | IBFirstResponder 34 | IBIPadFramework 35 | 36 | 37 | 38 | 268 39 | {768, 1024} 40 | 41 | 42 | _NS:710 43 | YES 44 | YES 45 | IBIPadFramework 46 | 47 | 48 | 49 | 50 | YES 51 | 52 | 53 | view 54 | 55 | 56 | 57 | 9 58 | 59 | 60 | 61 | pageView 62 | 63 | 64 | 65 | 10 66 | 67 | 68 | 69 | 70 | YES 71 | 72 | 0 73 | 74 | YES 75 | 76 | 77 | 78 | 79 | 80 | -1 81 | 82 | 83 | File's Owner 84 | 85 | 86 | -2 87 | 88 | 89 | 90 | 91 | 6 92 | 93 | 94 | 95 | 96 | 97 | 98 | YES 99 | 100 | YES 101 | -1.CustomClassName 102 | -1.IBPluginDependency 103 | -2.CustomClassName 104 | -2.IBPluginDependency 105 | 6.CustomClassName 106 | 6.IBPluginDependency 107 | 108 | 109 | YES 110 | PageViewController 111 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 112 | UIResponder 113 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 114 | PageView 115 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 116 | 117 | 118 | 119 | YES 120 | 121 | 122 | 123 | 124 | 125 | YES 126 | 127 | 128 | 129 | 130 | 10 131 | 132 | 133 | 134 | YES 135 | 136 | PageView 137 | UIScrollView 138 | 139 | IBProjectSource 140 | ./Classes/PageView.h 141 | 142 | 143 | 144 | PageViewController 145 | UIViewController 146 | 147 | pageView 148 | PageView 149 | 150 | 151 | pageView 152 | 153 | pageView 154 | PageView 155 | 156 | 157 | 158 | IBProjectSource 159 | ./Classes/PageViewController.h 160 | 161 | 162 | 163 | 164 | 0 165 | IBIPadFramework 166 | 167 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 168 | 169 | 170 | YES 171 | 3 172 | 911 173 | 174 | 175 | -------------------------------------------------------------------------------- /PDFKitten/RenderingState.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Font.h" 3 | 4 | @interface RenderingState : NSObject { 5 | CGAffineTransform lineMatrix; 6 | CGAffineTransform textMatrix; 7 | CGAffineTransform ctm; 8 | CGFloat leading; 9 | CGFloat wordSpacing; 10 | CGFloat characterSpacing; 11 | CGFloat horizontalScaling; 12 | CGFloat textRise; 13 | Font *font; 14 | CGFloat fontSize; 15 | } 16 | 17 | /* Set the text matrix and (optionally) the line matrix */ 18 | - (void)setTextMatrix:(CGAffineTransform)matrix replaceLineMatrix:(BOOL)replace; 19 | 20 | /* Transform the text matrix */ 21 | - (void)translateTextPosition:(CGSize)size; 22 | 23 | /* Move to start of next line, optionally with custom line height and indent, and optionally save line height */ 24 | - (void)newLineWithLeading:(CGFloat)aLeading indent:(CGFloat)indent save:(BOOL)save; 25 | - (void)newLineWithLeading:(CGFloat)lineHeight save:(BOOL)save; 26 | - (void)newLine; 27 | 28 | /* Converts a size from text space to user space */ 29 | - (CGSize)convertSizeToUserSpace:(CGSize)aSize; 30 | 31 | /* Converts a float to user space */ 32 | - (CGFloat)convertToUserSpace:(CGFloat)value; 33 | 34 | 35 | /* Matrixes (line-, text- and global) */ 36 | @property (nonatomic, assign) CGAffineTransform lineMatrix; 37 | @property (nonatomic, assign) CGAffineTransform textMatrix; 38 | @property (nonatomic, assign) CGAffineTransform ctm; 39 | 40 | /* Text size, spacing, scaling etc. */ 41 | @property (nonatomic, assign) CGFloat characterSpacing; 42 | @property (nonatomic, assign) CGFloat wordSpacing; 43 | @property (nonatomic, assign) CGFloat leadning; 44 | @property (nonatomic, assign) CGFloat textRise; 45 | @property (nonatomic, assign) CGFloat horizontalScaling; 46 | 47 | /* Font and font size */ 48 | @property (nonatomic, retain) Font *font; 49 | @property (nonatomic, assign) CGFloat fontSize; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /PDFKitten/RenderingState.m: -------------------------------------------------------------------------------- 1 | #import "RenderingState.h" 2 | 3 | #define kGlyphSpaceScale 1000 4 | 5 | @implementation RenderingState 6 | 7 | - (id)init 8 | { 9 | if ((self = [super init])) 10 | { 11 | // Default values 12 | self.textMatrix = CGAffineTransformIdentity; 13 | self.lineMatrix = CGAffineTransformIdentity; 14 | self.ctm = CGAffineTransformIdentity; 15 | self.horizontalScaling = 1.0; 16 | } 17 | return self; 18 | } 19 | 20 | - (id)copyWithZone:(NSZone *)zone 21 | { 22 | RenderingState *copy = [[RenderingState alloc] init]; 23 | copy.lineMatrix = self.lineMatrix; 24 | copy.textMatrix = self.textMatrix; 25 | copy.leadning = self.leadning; 26 | copy.wordSpacing = self.wordSpacing; 27 | copy.characterSpacing = self.characterSpacing; 28 | copy.horizontalScaling = self.horizontalScaling; 29 | copy.textRise = self.textRise; 30 | copy.font = self.font; 31 | copy.fontSize = self.fontSize; 32 | copy.ctm = self.ctm; 33 | return copy; 34 | } 35 | 36 | /* Set the text matrix, and optionally the line matrix */ 37 | - (void)setTextMatrix:(CGAffineTransform)matrix replaceLineMatrix:(BOOL)replace 38 | { 39 | self.textMatrix = matrix; 40 | if (replace) 41 | { 42 | self.lineMatrix = matrix; 43 | } 44 | } 45 | 46 | /* Moves the text cursor forward */ 47 | - (void)translateTextPosition:(CGSize)size 48 | { 49 | self.textMatrix = CGAffineTransformTranslate(self.textMatrix, size.width, size.height); 50 | } 51 | 52 | /* Move to start of next line, with custom line height and relative indent */ 53 | - (void)newLineWithLeading:(CGFloat)aLeading indent:(CGFloat)indent save:(BOOL)save 54 | { 55 | CGAffineTransform t = CGAffineTransformTranslate(self.lineMatrix, indent, -aLeading); 56 | [self setTextMatrix:t replaceLineMatrix:YES]; 57 | if (save) 58 | { 59 | self.leadning = aLeading; 60 | } 61 | } 62 | 63 | /* Transforms the rendering state to the start of the next line, with custom line height */ 64 | - (void)newLineWithLeading:(CGFloat)lineHeight save:(BOOL)save 65 | { 66 | [self newLineWithLeading:lineHeight indent:0 save:save]; 67 | } 68 | 69 | /* Transforms the rendering state to the start of the next line */ 70 | - (void)newLine 71 | { 72 | [self newLineWithLeading:self.leadning save:NO]; 73 | } 74 | 75 | /* Convert value to user space */ 76 | - (CGFloat)convertToUserSpace:(CGFloat)value 77 | { 78 | return value * (self.fontSize / kGlyphSpaceScale); 79 | } 80 | 81 | /* Converts a size from text space to user space */ 82 | - (CGSize)convertSizeToUserSpace:(CGSize)aSize 83 | { 84 | aSize.width = [self convertToUserSpace:aSize.width]; 85 | aSize.height = [self convertToUserSpace:aSize.height]; 86 | return aSize; 87 | } 88 | 89 | @synthesize characterSpacing, wordSpacing, leadning, textRise, horizontalScaling, font, fontSize, lineMatrix, textMatrix, ctm; 90 | 91 | @end -------------------------------------------------------------------------------- /PDFKitten/RenderingStateStack.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class RenderingState; 4 | 5 | @interface RenderingStateStack : NSObject { 6 | NSMutableArray *stack; 7 | } 8 | 9 | + (RenderingStateStack *)stack; 10 | 11 | /* Push a rendering state to the stack */ 12 | - (void)pushRenderingState:(RenderingState *)state; 13 | 14 | /* Pops the top rendering state off the stack */ 15 | - (RenderingState *)popRenderingState; 16 | 17 | /* The rendering state currently on top of the stack */ 18 | @property (nonatomic, readonly) RenderingState *topRenderingState; 19 | 20 | @end -------------------------------------------------------------------------------- /PDFKitten/RenderingStateStack.m: -------------------------------------------------------------------------------- 1 | #import "RenderingStateStack.h" 2 | #import "RenderingState.h" 3 | 4 | @implementation RenderingStateStack 5 | 6 | + (RenderingStateStack *)stack { 7 | return [[RenderingStateStack alloc] init]; 8 | } 9 | 10 | - (id)init 11 | { 12 | if ((self = [super init])) 13 | { 14 | stack = [[NSMutableArray alloc] init]; 15 | RenderingState *rootRenderingState = [[RenderingState alloc] init]; 16 | [self pushRenderingState:rootRenderingState]; 17 | } 18 | return self; 19 | } 20 | 21 | /* The rendering state currently on top of the stack */ 22 | - (RenderingState *)topRenderingState 23 | { 24 | return [stack lastObject]; 25 | } 26 | 27 | /* Push a rendering state to the stack */ 28 | - (void)pushRenderingState:(RenderingState *)state 29 | { 30 | [stack addObject:state]; 31 | } 32 | 33 | /* Pops the top rendering state off the stack */ 34 | - (RenderingState *)popRenderingState 35 | { 36 | RenderingState *state = [stack lastObject]; 37 | [stack removeLastObject]; 38 | 39 | return state; 40 | } 41 | 42 | @end -------------------------------------------------------------------------------- /PDFKitten/RootViewController.h: -------------------------------------------------------------------------------- 1 | #import "PageViewController.h" 2 | #import "PageView.h" 3 | 4 | @interface RootViewController : UIViewController { 5 | CGPDFDocumentRef document; 6 | UIPopoverController *libraryPopover; 7 | IBOutlet PageView *pageView; 8 | IBOutlet UISearchBar *searchBar; 9 | NSString *keyword; 10 | } 11 | 12 | @property (nonatomic, readonly) NSString *documentPath; 13 | @end 14 | -------------------------------------------------------------------------------- /PDFKitten/RootViewController.m: -------------------------------------------------------------------------------- 1 | #import "RootViewController.h" 2 | #import "PDFPage.h" 3 | #import "DocumentsView.h" 4 | #import "Scanner.h" 5 | #import "PDFPageDetailsView.h" 6 | 7 | @implementation RootViewController 8 | 9 | #pragma mark - Initialization 10 | 11 | - (id)initWithCoder:(NSCoder *)aDecoder 12 | { 13 | if ((self = [super initWithCoder:aDecoder])) 14 | { 15 | NSURL *pdfURL = [NSURL fileURLWithPath:self.documentPath]; 16 | document = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL); 17 | } 18 | return self; 19 | } 20 | 21 | - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController 22 | { 23 | if ([popoverController isEqual:libraryPopover]) 24 | { 25 | libraryPopover = nil; 26 | } 27 | } 28 | 29 | - (void)didSelectDocument:(NSURL *)url 30 | { 31 | [libraryPopover dismissPopoverAnimated:YES]; 32 | libraryPopover = nil; 33 | 34 | CGPDFDocumentRelease(document); 35 | document = CGPDFDocumentCreateWithURL((CFURLRef)url); 36 | [pageView reloadData]; 37 | } 38 | 39 | - (IBAction)showLibraryPopover:(UIBarButtonItem *)sender 40 | { 41 | if (libraryPopover) 42 | { 43 | [libraryPopover dismissPopoverAnimated:NO]; 44 | libraryPopover = nil; 45 | return; 46 | } 47 | 48 | DocumentsView *docView = [[DocumentsView alloc] init]; 49 | docView.delegate = self; 50 | libraryPopover = [[UIPopoverController alloc] initWithContentViewController:docView]; 51 | libraryPopover.delegate = self; 52 | [libraryPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES]; 53 | } 54 | 55 | - (void)viewDidAppear:(BOOL)animated 56 | { 57 | [super viewDidAppear:animated]; 58 | } 59 | 60 | #pragma mark PageViewDelegate 61 | 62 | /* The number of pages in the current PDF document */ 63 | - (NSInteger)numberOfPagesInPageView:(PageView *)pageView 64 | { 65 | return CGPDFDocumentGetNumberOfPages(document); 66 | } 67 | 68 | - (FontCollection *)activeFontCollection 69 | { 70 | Page *page = [pageView pageAtIndex:pageView.page]; 71 | PDFContentView *pdfPage = (PDFContentView *) [(PDFPage *) page contentView]; 72 | return [[pdfPage scanner] fontCollection]; 73 | } 74 | 75 | /* Return the detailed view corresponding to a page */ 76 | - (PDFPageDetailsView *)pageView:(PageView *)aPageView detailedViewForPage:(NSInteger)page 77 | { 78 | FontCollection *collection = [self activeFontCollection]; 79 | PDFPageDetailsView *detailedView = [[PDFPageDetailsView alloc] initWithFont:collection]; 80 | return detailedView; 81 | } 82 | 83 | // TODO: Assign page to either the page or its content view, not both. 84 | 85 | /* Page view object for the requested page */ 86 | - (Page *)pageView:(PageView *)aPageView viewForPage:(NSInteger)pageNumber 87 | { 88 | PDFPage *page = (PDFPage *) [aPageView dequeueRecycledPage]; 89 | if (!page) 90 | { 91 | page = [[PDFPage alloc] initWithFrame:CGRectZero]; 92 | } 93 | 94 | page.pageNumber = pageNumber; 95 | CGPDFPageRef pdfPage = CGPDFDocumentGetPage(document, pageNumber + 1); // PDF document page numbers are 1-based 96 | [page setPage:pdfPage]; 97 | page.keyword = keyword; 98 | 99 | return page; 100 | } 101 | 102 | - (NSString *)keywordForPageView:(PageView *)pageView 103 | { 104 | return keyword; 105 | } 106 | 107 | // TODO: add user interface for choosing document 108 | 109 | - (NSString *)documentPath 110 | { 111 | return [[NSBundle mainBundle] pathForResource:@"Kurt the Cat" ofType:@"pdf"]; 112 | } 113 | 114 | #pragma mark Search 115 | 116 | - (void)searchBarSearchButtonClicked:(UISearchBar *)aSearchBar 117 | { 118 | keyword = [aSearchBar text]; 119 | [pageView setKeyword:keyword]; 120 | 121 | [aSearchBar resignFirstResponder]; 122 | } 123 | 124 | #pragma mark Memory Management 125 | 126 | - (void)dealloc 127 | { 128 | CGPDFDocumentRelease(document); 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /PDFKitten/Samples/Greek.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KurtCode/PDFKitten/0b0d6be44097df9d5ce91b3f912b57876b56335e/PDFKitten/Samples/Greek.pdf -------------------------------------------------------------------------------- /PDFKitten/Samples/Kurt the Cat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KurtCode/PDFKitten/0b0d6be44097df9d5ce91b3f912b57876b56335e/PDFKitten/Samples/Kurt the Cat.pdf -------------------------------------------------------------------------------- /PDFKitten/Scanner.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "StringDetector.h" 3 | #import "FontCollection.h" 4 | #import "RenderingState.h" 5 | #import "Selection.h" 6 | #import "RenderingStateStack.h" 7 | 8 | @interface Scanner : NSObject { 9 | CGPDFPageRef pdfPage; 10 | NSMutableArray *selections; 11 | Selection *possibleSelection; 12 | 13 | StringDetector *stringDetector; 14 | FontCollection *fontCollection; 15 | RenderingStateStack *renderingStateStack; 16 | NSMutableString *content; 17 | } 18 | 19 | + (Scanner *)scannerWithPage:(CGPDFPageRef)page; 20 | 21 | - (NSArray *)select:(NSString *)keyword; 22 | 23 | @property (nonatomic, readonly) RenderingState *renderingState; 24 | 25 | @property (nonatomic, retain) RenderingStateStack *renderingStateStack; 26 | @property (nonatomic, retain) FontCollection *fontCollection; 27 | @property (nonatomic, retain) StringDetector *stringDetector; 28 | @property (nonatomic, retain) NSMutableString *content; 29 | 30 | 31 | @property (nonatomic, retain) NSMutableArray *selections; 32 | @end 33 | -------------------------------------------------------------------------------- /PDFKitten/Scanner.m: -------------------------------------------------------------------------------- 1 | #import "Scanner.h" 2 | #import "pdfScannerCallbacks.mm" 3 | 4 | @implementation Scanner 5 | 6 | + (Scanner *)scannerWithPage:(CGPDFPageRef)page { 7 | return [[Scanner alloc] initWithPage:page]; 8 | } 9 | 10 | - (id)initWithPage:(CGPDFPageRef)page { 11 | if (self = [super init]) { 12 | pdfPage = page; 13 | self.fontCollection = [self fontCollectionWithPage:pdfPage]; 14 | self.selections = [NSMutableArray array]; 15 | } 16 | 17 | return self; 18 | } 19 | 20 | - (NSArray *)select:(NSString *)keyword { 21 | self.content = [NSMutableString string]; 22 | self.stringDetector = [StringDetector detectorWithKeyword:keyword delegate:self]; 23 | [self.selections removeAllObjects]; 24 | self.renderingStateStack = [RenderingStateStack stack]; 25 | 26 | CGPDFOperatorTableRef operatorTable = [self newOperatorTable]; 27 | CGPDFContentStreamRef contentStream = CGPDFContentStreamCreateWithPage(pdfPage); 28 | CGPDFScannerRef scanner = CGPDFScannerCreate(contentStream, operatorTable, (__bridge void *)(self)); 29 | CGPDFScannerScan(scanner); 30 | 31 | CGPDFScannerRelease(scanner); 32 | CGPDFContentStreamRelease(contentStream); 33 | CGPDFOperatorTableRelease(operatorTable); 34 | 35 | self.stringDetector.delegate = nil; 36 | self.stringDetector = nil; 37 | 38 | return self.selections; 39 | } 40 | 41 | - (CGPDFOperatorTableRef)newOperatorTable { 42 | CGPDFOperatorTableRef operatorTable = CGPDFOperatorTableCreate(); 43 | 44 | // Text-showing operators 45 | CGPDFOperatorTableSetCallback(operatorTable, "Tj", printString); 46 | CGPDFOperatorTableSetCallback(operatorTable, "\'", printStringNewLine); 47 | CGPDFOperatorTableSetCallback(operatorTable, "\"", printStringNewLineSetSpacing); 48 | CGPDFOperatorTableSetCallback(operatorTable, "TJ", printStringsAndSpaces); 49 | 50 | // Text-positioning operators 51 | CGPDFOperatorTableSetCallback(operatorTable, "Tm", setTextMatrix); 52 | CGPDFOperatorTableSetCallback(operatorTable, "Td", newLineWithLeading); 53 | CGPDFOperatorTableSetCallback(operatorTable, "TD", newLineSetLeading); 54 | CGPDFOperatorTableSetCallback(operatorTable, "T*", newLine); 55 | 56 | // Text state operators 57 | CGPDFOperatorTableSetCallback(operatorTable, "Tw", setWordSpacing); 58 | CGPDFOperatorTableSetCallback(operatorTable, "Tc", setCharacterSpacing); 59 | CGPDFOperatorTableSetCallback(operatorTable, "TL", setTextLeading); 60 | CGPDFOperatorTableSetCallback(operatorTable, "Tz", setHorizontalScale); 61 | CGPDFOperatorTableSetCallback(operatorTable, "Ts", setTextRise); 62 | CGPDFOperatorTableSetCallback(operatorTable, "Tf", setFont); 63 | 64 | // Graphics state operators 65 | CGPDFOperatorTableSetCallback(operatorTable, "cm", applyTransformation); 66 | CGPDFOperatorTableSetCallback(operatorTable, "q", pushRenderingState); 67 | CGPDFOperatorTableSetCallback(operatorTable, "Q", popRenderingState); 68 | 69 | CGPDFOperatorTableSetCallback(operatorTable, "BT", newParagraph); 70 | 71 | return operatorTable; 72 | } 73 | 74 | /* Create a font dictionary given a PDF page */ 75 | - (FontCollection *)fontCollectionWithPage:(CGPDFPageRef)page { 76 | CGPDFDictionaryRef dict = CGPDFPageGetDictionary(page); 77 | if (!dict) { 78 | NSLog(@"Scanner: fontCollectionWithPage: page dictionary missing"); 79 | return nil; 80 | } 81 | 82 | CGPDFDictionaryRef resources; 83 | if (!CGPDFDictionaryGetDictionary(dict, "Resources", &resources)) { 84 | NSLog(@"Scanner: fontCollectionWithPage: page dictionary missing Resources dictionary"); 85 | return nil; 86 | } 87 | 88 | CGPDFDictionaryRef fonts; 89 | if (!CGPDFDictionaryGetDictionary(resources, "Font", &fonts)) { 90 | return nil; 91 | } 92 | 93 | FontCollection *collection = [[FontCollection alloc] initWithFontDictionary:fonts]; 94 | return collection; 95 | } 96 | 97 | - (void)detector:(StringDetector *)detector didScanCharacter:(unichar)character { 98 | Font *font = self.renderingState.font; 99 | unichar cid = character; 100 | 101 | if (font.toUnicode) 102 | { 103 | cid = [font.toUnicode cidCharacter:character]; 104 | } 105 | 106 | CGFloat width = [font widthOfCharacter:cid withFontSize:self.renderingState.fontSize]; 107 | width /= 1000; 108 | width += self.renderingState.characterSpacing; 109 | 110 | if (character == 32) 111 | { 112 | width += self.renderingState.wordSpacing; 113 | } 114 | 115 | [self.renderingState translateTextPosition:CGSizeMake(width, 0)]; 116 | } 117 | 118 | - (void)detectorDidStartMatching:(StringDetector *)detector 119 | { 120 | possibleSelection = [Selection selectionWithState:self.renderingState]; 121 | } 122 | 123 | - (void)detectorFoundString:(StringDetector *)detector 124 | { 125 | if (possibleSelection) 126 | { 127 | possibleSelection.finalState = self.renderingState; 128 | [self.selections addObject:possibleSelection]; 129 | possibleSelection = nil; 130 | } 131 | } 132 | 133 | - (RenderingState *)renderingState 134 | { 135 | return [self.renderingStateStack topRenderingState]; 136 | } 137 | 138 | @synthesize stringDetector, fontCollection, renderingStateStack, content, selections, renderingState; 139 | 140 | @end -------------------------------------------------------------------------------- /PDFKitten/Selection.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class RenderingState; 4 | 5 | @interface Selection : NSObject 6 | 7 | + (Selection *)selectionWithState:(RenderingState *)state; 8 | 9 | @property (nonatomic, readonly) CGRect frame; 10 | @property (nonatomic, readonly) CGAffineTransform transform; 11 | 12 | @property (nonatomic, copy) RenderingState *initialState; 13 | @property (nonatomic, copy) RenderingState *finalState; 14 | 15 | @property (nonatomic, readonly) CGFloat height; 16 | @property (nonatomic, readonly) CGFloat width; 17 | @property (nonatomic, readonly) CGFloat descent; 18 | @property (nonatomic, readonly) CGFloat ascent; 19 | @end 20 | -------------------------------------------------------------------------------- /PDFKitten/Selection.m: -------------------------------------------------------------------------------- 1 | #import "Selection.h" 2 | #import "RenderingState.h" 3 | 4 | CGFloat horizontal(CGAffineTransform transform) { 5 | return transform.tx / transform.a; 6 | } 7 | 8 | @implementation Selection 9 | 10 | + (Selection *)selectionWithState:(RenderingState *)state 11 | { 12 | Selection *selection = [[Selection alloc] init]; 13 | selection.initialState = state; 14 | return selection; 15 | } 16 | 17 | - (CGAffineTransform)transform 18 | { 19 | return CGAffineTransformConcat([self.initialState textMatrix], [self.initialState ctm]); 20 | } 21 | 22 | - (CGRect)frame 23 | { 24 | return CGRectMake(0, self.descent, self.width, self.height); 25 | } 26 | 27 | - (CGFloat)height 28 | { 29 | return self.ascent - self.descent; 30 | } 31 | 32 | - (CGFloat)width 33 | { 34 | return horizontal(self.finalState.textMatrix) - horizontal(self.initialState.textMatrix); 35 | } 36 | 37 | - (CGFloat)ascent 38 | { 39 | return MAX([self ascentInUserSpace:self.initialState], [self ascentInUserSpace:self.finalState]); 40 | } 41 | 42 | - (CGFloat)descent 43 | { 44 | return MIN([self descentInUserSpace:self.initialState], [self descentInUserSpace:self.finalState]); 45 | } 46 | 47 | - (CGFloat)ascentInUserSpace:(RenderingState *)state 48 | { 49 | return state.font.fontDescriptor.ascent * state.fontSize / 1000; 50 | } 51 | 52 | - (CGFloat)descentInUserSpace:(RenderingState *)state 53 | { 54 | return state.font.fontDescriptor.descent * state.fontSize / 1000; 55 | } 56 | 57 | @synthesize frame, transform; 58 | 59 | @end -------------------------------------------------------------------------------- /PDFKitten/SimpleFont.h: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple font is one of the following types: 3 | * - Type1 4 | * - Type3 5 | * - TrueType 6 | * - MMType1 7 | * 8 | * All simple fonts have the following specific traits: 9 | * - Encoding 10 | * - Widths (custom implementation) 11 | * 12 | */ 13 | 14 | #import 15 | #import "Font.h" 16 | 17 | @interface SimpleFont : Font { 18 | } 19 | 20 | /* Custom implementation for all simple fonts */ 21 | - (void)setWidthsWithFontDictionary:(CGPDFDictionaryRef)dict; 22 | 23 | /* Set encoding with name or dictionary */ 24 | - (void)setEncodingWithEncodingObject:(CGPDFObjectRef)object; 25 | 26 | /* Set encoding, given a font dictionary */ 27 | - (void)setEncodingWithFontDictionary:(CGPDFDictionaryRef)dict; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /PDFKitten/SimpleFont.m: -------------------------------------------------------------------------------- 1 | #import "SimpleFont.h" 2 | 3 | 4 | @implementation SimpleFont 5 | 6 | /* Initialize with a font dictionary */ 7 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 8 | { 9 | if ((self = [super initWithFontDictionary:dict])) 10 | { 11 | // Set encoding for any font 12 | [self setEncodingWithFontDictionary:dict]; 13 | } 14 | return self; 15 | } 16 | 17 | /* Custom implementation for all simple fonts */ 18 | - (void)setWidthsWithFontDictionary:(CGPDFDictionaryRef)dict 19 | { 20 | CGPDFArrayRef array; 21 | if (!CGPDFDictionaryGetArray(dict, "Widths", &array)) return; 22 | size_t count = CGPDFArrayGetCount(array); 23 | CGPDFInteger firstChar, lastChar; 24 | if (!CGPDFDictionaryGetInteger(dict, "FirstChar", &firstChar)) return; 25 | if (!CGPDFDictionaryGetInteger(dict, "LastChar", &lastChar)) return; 26 | widthsRange = NSMakeRange(firstChar, lastChar-firstChar); 27 | NSMutableDictionary *widthsDict = [NSMutableDictionary dictionary]; 28 | for (int i = 0; i < count; i++) 29 | { 30 | CGPDFReal width; 31 | if (!CGPDFArrayGetNumber(array, i, &width)) continue; 32 | NSNumber *key = [NSNumber numberWithInt:firstChar+i]; 33 | NSNumber *value = [NSNumber numberWithFloat:width]; 34 | [widthsDict setObject:value forKey:key]; 35 | } 36 | self.widths = widthsDict; 37 | } 38 | 39 | /* Set encoding, given a font dictionary */ 40 | - (void)setEncodingWithFontDictionary:(CGPDFDictionaryRef)dict 41 | { 42 | CGPDFObjectRef encodingObject; 43 | if (!CGPDFDictionaryGetObject(dict, "Encoding", &encodingObject)) return; 44 | [self setEncodingWithEncodingObject:encodingObject]; 45 | } 46 | 47 | /* Custom implementation for all simple fonts */ 48 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 49 | { 50 | const unsigned char *bytes = CGPDFStringGetBytePtr(pdfString); 51 | NSUInteger length = CGPDFStringGetLength(pdfString); 52 | if (!self.encoding && self.toUnicode) 53 | { 54 | // Use ToUnicode map 55 | NSMutableString *unicodeString = [NSMutableString string]; 56 | 57 | // Translate to Unicode 58 | for (int i = 0; i < length; i++) 59 | { 60 | unichar cid = bytes[i]; 61 | unichar uni = [self.toUnicode unicodeCharacter:cid]; 62 | //NSLog(@"(%hu) %C -> (%hu) %C", cid, cid, uni, uni); 63 | [unicodeString appendFormat:@"%C", uni]; 64 | } 65 | 66 | return unicodeString; 67 | } 68 | else if (!self.encoding) 69 | { 70 | return [super stringWithPDFString:pdfString]; 71 | } 72 | NSData *rawBytes = [NSData dataWithBytes:bytes length:length]; 73 | NSString *string = [[NSString alloc] initWithData:rawBytes encoding:nativeEncoding(self.encoding)]; 74 | 75 | return string; 76 | } 77 | 78 | /* Set encoding with name or dictionary */ 79 | - (void)setEncodingWithEncodingObject:(CGPDFObjectRef)object 80 | { 81 | CGPDFObjectType type = CGPDFObjectGetType(object); 82 | 83 | /* Encoding dictionary with base encoding and differences */ 84 | if (type == kCGPDFObjectTypeDictionary) 85 | { 86 | /* NOTE: Also needs to capture differences */ 87 | CGPDFDictionaryRef dict = nil; 88 | if (!CGPDFObjectGetValue(object, kCGPDFObjectTypeDictionary, &dict)) return; 89 | CGPDFObjectRef baseEncoding = nil; 90 | if (!CGPDFDictionaryGetObject(dict, "BaseEncoding", &baseEncoding)) return; 91 | [self setEncodingWithEncodingObject:baseEncoding]; 92 | return; 93 | } 94 | 95 | /* Only accept name objects */ 96 | if (type != kCGPDFObjectTypeName) return; 97 | 98 | const char *name; 99 | if (!CGPDFObjectGetValue(object, kCGPDFObjectTypeName, &name)) return; 100 | 101 | if (strcmp(name, "MacRomanEncoding") == 0) 102 | { 103 | self.encoding = MacRomanEncoding; 104 | } 105 | else if (strcmp(name, "MacExpertEncoding") == 0) 106 | { 107 | // What is MacExpertEncoding ?? 108 | self.encoding = MacRomanEncoding; 109 | } 110 | else if (strcmp(name, "WinAnsiEncoding") == 0) 111 | { 112 | self.encoding = WinAnsiEncoding; 113 | } 114 | } 115 | 116 | /* Unicode character with CID */ 117 | //- (NSString *)stringWithCharacters:(const char *)characters 118 | //{ 119 | // return [NSString stringWithCString:characters encoding:encoding]; 120 | //} 121 | 122 | - (CGFloat)widthOfSpace 123 | { 124 | unichar c = 0x20; 125 | 126 | if (self.toUnicode) 127 | { 128 | c = [self.toUnicode cidCharacter:c]; 129 | 130 | if (c == NSNotFound) 131 | { 132 | return 0; 133 | } 134 | } 135 | 136 | return [self widthOfCharacter:c withFontSize:1.0]; 137 | } 138 | 139 | @end -------------------------------------------------------------------------------- /PDFKitten/StringDetector.h: -------------------------------------------------------------------------------- 1 | /** 2 | * A detector implementing a finite state machine with the goal of detecting a predefined keyword in a continuous stream 3 | * of characters. The user of a detector can append strings, and will receive a number of messages reflecting the 4 | * current state of the detector. 5 | */ 6 | 7 | #import 8 | #import "Font.h" 9 | #import "StringDetectorDelegate.h" 10 | 11 | @class StringDetector; 12 | 13 | @interface StringDetector : NSObject { 14 | NSString *keyword; 15 | NSUInteger keywordPosition; 16 | NSMutableString *unicodeContent; 17 | __weak id delegate; 18 | } 19 | 20 | + (StringDetector *)detectorWithKeyword:(NSString *)keyword delegate:(id)delegate; 21 | - (id)initWithKeyword:(NSString *)needle; 22 | - (void)setKeyword:(NSString *)kword; 23 | - (void)reset; 24 | 25 | - (NSString *)appendString:(NSString *)inputString; 26 | 27 | @property (nonatomic, weak) id delegate; 28 | @property (nonatomic, retain) NSMutableString *unicodeContent; 29 | 30 | @end -------------------------------------------------------------------------------- /PDFKitten/StringDetector.m: -------------------------------------------------------------------------------- 1 | #import "StringDetector.h" 2 | 3 | @implementation StringDetector 4 | 5 | + (StringDetector *)detectorWithKeyword:(NSString *)keyword delegate:(id)delegate 6 | { 7 | StringDetector *detector = [[StringDetector alloc] initWithKeyword:keyword]; 8 | detector.delegate = delegate; 9 | return detector; 10 | } 11 | 12 | - (id)initWithKeyword:(NSString *)string 13 | { 14 | if (self = [super init]) 15 | { 16 | keyword = [string lowercaseString]; 17 | self.unicodeContent = [NSMutableString string]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (NSString *)appendString:(NSString *)inputString 24 | { 25 | NSString *lowercaseString = [inputString lowercaseString]; 26 | int position = 0; 27 | 28 | if (lowercaseString) 29 | { 30 | [unicodeContent appendString:lowercaseString]; 31 | } 32 | 33 | while (position < inputString.length) 34 | { 35 | unichar inputCharacter = [inputString characterAtIndex:position]; 36 | unichar actualCharacter = [lowercaseString characterAtIndex:position++]; 37 | unichar expectedCharacter = [keyword characterAtIndex:keywordPosition]; 38 | 39 | if (actualCharacter != expectedCharacter) 40 | { 41 | if (keywordPosition > 0) 42 | { 43 | // Read character again 44 | position--; 45 | } 46 | else if ([delegate respondsToSelector:@selector(detector:didScanCharacter:)]) 47 | { 48 | [delegate detector:self didScanCharacter:inputCharacter]; 49 | } 50 | 51 | // Reset keyword position 52 | keywordPosition = 0; 53 | continue; 54 | } 55 | 56 | if (keywordPosition == 0 && [delegate respondsToSelector:@selector(detectorDidStartMatching:)]) 57 | { 58 | [delegate detectorDidStartMatching:self]; 59 | } 60 | 61 | if ([delegate respondsToSelector:@selector(detector:didScanCharacter:)]) 62 | { 63 | [delegate detector:self didScanCharacter:inputCharacter]; 64 | } 65 | 66 | if (++keywordPosition < keyword.length) 67 | { 68 | // Keep matching keyword 69 | continue; 70 | } 71 | 72 | // Reset keyword position 73 | keywordPosition = 0; 74 | 75 | if ([delegate respondsToSelector:@selector(detectorFoundString:)]) 76 | { 77 | [delegate detectorFoundString:self]; 78 | } 79 | } 80 | 81 | return inputString; 82 | } 83 | 84 | - (void)setKeyword:(NSString *)kword 85 | { 86 | keyword = [kword lowercaseString]; 87 | keywordPosition = 0; 88 | } 89 | 90 | - (void)reset 91 | { 92 | keywordPosition = 0; 93 | } 94 | 95 | @synthesize delegate, unicodeContent; 96 | 97 | @end -------------------------------------------------------------------------------- /PDFKitten/StringDetectorDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class StringDetector; 4 | 5 | @protocol StringDetectorDelegate 6 | @optional 7 | - (void)detectorDidStartMatching:(StringDetector *)stringDetector; 8 | - (void)detectorFoundString:(StringDetector *)detector; 9 | - (void)detector:(StringDetector *)detector didScanCharacter:(unichar)character; 10 | @end 11 | -------------------------------------------------------------------------------- /PDFKitten/TrueTypeFont.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SimpleFont.h" 3 | 4 | 5 | @interface TrueTypeFont : SimpleFont { 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/TrueTypeFont.m: -------------------------------------------------------------------------------- 1 | #import "TrueTypeFont.h" 2 | 3 | 4 | @implementation TrueTypeFont 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /PDFKitten/Type0Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "CompositeFont.h" 3 | 4 | @interface Type0Font : CompositeFont { 5 | NSMutableArray *descendantFonts; 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/Type0Font.m: -------------------------------------------------------------------------------- 1 | #import "Type0Font.h" 2 | #import "CIDType0Font.h" 3 | #import "CIDType2Font.h" 4 | 5 | 6 | @interface Type0Font () 7 | @property (nonatomic, readonly) NSMutableArray *descendantFonts; 8 | @end 9 | 10 | @implementation Type0Font 11 | 12 | /* Initialize with font dictionary */ 13 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 14 | { 15 | if ((self = [super initWithFontDictionary:dict])) 16 | { 17 | CGPDFArrayRef dFonts; 18 | if (CGPDFDictionaryGetArray(dict, "DescendantFonts", &dFonts)) 19 | { 20 | NSUInteger count = CGPDFArrayGetCount(dFonts); 21 | for (int i = 0; i < count; i++) 22 | { 23 | CGPDFDictionaryRef fontDict; 24 | if (!CGPDFArrayGetDictionary(dFonts, i, &fontDict)) continue; 25 | const char *subtype; 26 | if (!CGPDFDictionaryGetName(fontDict, "Subtype", &subtype)) continue; 27 | 28 | NSLog(@"Descendant font type %s", subtype); 29 | 30 | if (strcmp(subtype, "CIDFontType0") == 0) 31 | { 32 | // Add descendant font of type 0 33 | CIDType0Font *font = [[CIDType0Font alloc] initWithFontDictionary:fontDict]; 34 | if (font) [self.descendantFonts addObject:font]; 35 | } 36 | else if (strcmp(subtype, "CIDFontType2") == 0) 37 | { 38 | // Add descendant font of type 2 39 | CIDType2Font *font = [[CIDType2Font alloc] initWithFontDictionary:fontDict]; 40 | if (font) [self.descendantFonts addObject:font]; 41 | } 42 | } 43 | } 44 | } 45 | return self; 46 | } 47 | 48 | /* Custom implementation, using descendant fonts */ 49 | - (CGFloat)widthOfCharacter:(unichar)characher withFontSize:(CGFloat)fontSize 50 | { 51 | for (Font *font in self.descendantFonts) 52 | { 53 | CGFloat width = [font widthOfCharacter:characher withFontSize:fontSize]; 54 | if (width > 0) return width; 55 | } 56 | return self.defaultWidth; 57 | } 58 | 59 | - (NSDictionary *)ligatures 60 | { 61 | return [[self.descendantFonts lastObject] ligatures]; 62 | } 63 | 64 | - (FontDescriptor *)fontDescriptor { 65 | Font *descendantFont = [self.descendantFonts lastObject]; 66 | return descendantFont.fontDescriptor; 67 | } 68 | 69 | - (CGFloat)minY 70 | { 71 | Font *descendantFont = [self.descendantFonts lastObject]; 72 | return [descendantFont.fontDescriptor descent]; 73 | } 74 | 75 | /* Highest point of any character */ 76 | - (CGFloat)maxY 77 | { 78 | Font *descendantFont = [self.descendantFonts lastObject]; 79 | return [descendantFont.fontDescriptor ascent]; 80 | } 81 | 82 | - (NSString *)stringWithPDFString:(CGPDFStringRef)pdfString 83 | { 84 | if (self.toUnicode) 85 | { 86 | size_t stringLength = CGPDFStringGetLength(pdfString); 87 | const unsigned char *characterCodes = CGPDFStringGetBytePtr(pdfString); 88 | NSMutableString *unicodeString = [NSMutableString string]; 89 | 90 | for (int i = 0; i < stringLength; i+=2) 91 | { 92 | unichar characterCode = characterCodes[i] << 8 | characterCodes[i+1]; 93 | unichar characterSelector = [self.toUnicode unicodeCharacter:characterCode]; 94 | [unicodeString appendFormat:@"%C", characterSelector]; 95 | } 96 | return unicodeString; 97 | } 98 | else if ([self.descendantFonts count] > 0) 99 | { 100 | Font *descendantFont = [self.descendantFonts lastObject]; 101 | return [descendantFont stringWithPDFString:pdfString]; 102 | } 103 | return @""; 104 | } 105 | 106 | - (NSString *)unicodeWithPDFString:(CGPDFStringRef)pdfString 107 | { 108 | NSMutableString *result; 109 | Font *descendantFont = [self.descendantFonts lastObject]; 110 | NSString *descendantResult = [descendantFont stringWithPDFString: pdfString]; 111 | 112 | if (self.toUnicode) 113 | { 114 | result = [[NSMutableString alloc] initWithCapacity: [descendantResult length]]; 115 | 116 | for (int i = 0; i < [descendantResult length]; i++) 117 | { 118 | unichar character = [self.toUnicode unicodeCharacter:[descendantResult characterAtIndex:i]]; 119 | [result appendFormat:@"%C", character]; 120 | } 121 | } 122 | else 123 | { 124 | result = [NSMutableString stringWithString: descendantResult]; 125 | } 126 | 127 | return result; 128 | } 129 | 130 | - (NSString *)cidWithPDFString:(CGPDFStringRef)pdfString { 131 | Font *descendantFont = [self.descendantFonts lastObject]; 132 | return [descendantFont stringWithPDFString: pdfString]; 133 | } 134 | 135 | #pragma mark Memory Management 136 | 137 | - (NSMutableArray *)descendantFonts 138 | { 139 | if (!descendantFonts) 140 | { 141 | descendantFonts = [[NSMutableArray alloc] init]; 142 | } 143 | 144 | return descendantFonts; 145 | } 146 | 147 | @end -------------------------------------------------------------------------------- /PDFKitten/Type1Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SimpleFont.h" 3 | 4 | @interface Type1Font : SimpleFont { 5 | } 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /PDFKitten/Type1Font.m: -------------------------------------------------------------------------------- 1 | #import "Type1Font.h" 2 | 3 | @implementation Type1Font 4 | 5 | - (id)initWithFontDictionary:(CGPDFDictionaryRef)dict 6 | { 7 | if (self = [super initWithFontDictionary:dict]) 8 | { 9 | } 10 | return self; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PDFKitten/Type3Font.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SimpleFont.h" 3 | 4 | @interface Type3Font : SimpleFont { 5 | 6 | } 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /PDFKitten/Type3Font.m: -------------------------------------------------------------------------------- 1 | #import "Type3Font.h" 2 | 3 | 4 | @implementation Type3Font 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /PDFKitten/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PDFKitten/en.lproj/MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C74 6 | 1938 7 | 1138.23 8 | 567.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | YES 15 | IBProxyObject 16 | IBUIBarButtonItem 17 | IBUIViewController 18 | IBUICustomObject 19 | IBUIToolbar 20 | IBUIWindow 21 | IBUISearchBar 22 | IBUIButton 23 | IBUIScrollView 24 | IBUIView 25 | 26 | 27 | YES 28 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 29 | 30 | 31 | PluginDependencyRecalculationVersion 32 | 33 | 34 | 35 | YES 36 | 37 | IBFilesOwner 38 | IBIPadFramework 39 | 40 | 41 | IBFirstResponder 42 | IBIPadFramework 43 | 44 | 45 | 46 | 292 47 | {768, 1024} 48 | 49 | 50 | 51 | 52 | 1 53 | MSAxIDEAA 54 | 55 | NO 56 | NO 57 | IBIPadFramework 58 | YES 59 | 60 | 61 | IBIPadFramework 62 | 63 | 64 | 65 | 66 | 274 67 | 68 | YES 69 | 70 | 71 | 258 72 | 73 | YES 74 | 75 | 76 | 290 77 | {{556, 0}, {200, 44}} 78 | 79 | 80 | 81 | _NS:598 82 | 3 83 | IBIPadFramework 84 | 85 | IBCocoaTouchFramework 86 | 87 | YES 88 | 89 | 90 | {768, 44} 91 | 92 | 93 | 94 | _NS:151 95 | NO 96 | NO 97 | IBIPadFramework 98 | 99 | YES 100 | 101 | Library 102 | IBIPadFramework 103 | 1 104 | 105 | 106 | 107 | IBIPadFramework 108 | 109 | 5 110 | 111 | 112 | IBIPadFramework 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 268 121 | {{0, 44}, {768, 980}} 122 | 123 | 124 | 125 | _NS:712 126 | YES 127 | YES 128 | IBIPadFramework 129 | 130 | 131 | 132 | 265 133 | {{740, 995}, {18, 19}} 134 | 135 | 136 | 137 | _NS:241 138 | NO 139 | IBIPadFramework 140 | 0 141 | 0 142 | 4 143 | YES 144 | 145 | 3 146 | MQA 147 | 148 | 149 | 1 150 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 151 | 152 | 153 | 3 154 | MC41AA 155 | 156 | 157 | 2 158 | 15 159 | 160 | 161 | Helvetica-Bold 162 | 15 163 | 16 164 | 165 | 166 | 167 | {768, 1024} 168 | 169 | 170 | 171 | _NS:49 172 | 173 | 3 174 | MQA 175 | 176 | 2 177 | 178 | 179 | IBIPadFramework 180 | 181 | 182 | 1 183 | 1 184 | 185 | IBIPadFramework 186 | NO 187 | 188 | 189 | 190 | 191 | YES 192 | 193 | 194 | delegate 195 | 196 | 197 | 198 | 9 199 | 200 | 201 | 202 | window 203 | 204 | 205 | 206 | 10 207 | 208 | 209 | 210 | rootViewController 211 | 212 | 213 | 214 | 22 215 | 216 | 217 | 218 | pageView 219 | 220 | 221 | 222 | 35 223 | 224 | 225 | 226 | searchBar 227 | 228 | 229 | 230 | 41 231 | 232 | 233 | 234 | showLibraryPopover: 235 | 236 | 237 | 238 | 34 239 | 240 | 241 | 242 | dataSource 243 | 244 | 245 | 246 | 33 247 | 248 | 249 | 250 | delegate 251 | 252 | 253 | 254 | 42 255 | 256 | 257 | 258 | detailedInfoButtonPressed: 259 | 260 | 261 | 7 262 | 263 | 44 264 | 265 | 266 | 267 | 268 | YES 269 | 270 | 0 271 | 272 | YES 273 | 274 | 275 | 276 | 277 | 278 | -1 279 | 280 | 281 | File's Owner 282 | 283 | 284 | -2 285 | 286 | 287 | 288 | 289 | 2 290 | 291 | 292 | YES 293 | 294 | 295 | 296 | 297 | 6 298 | 299 | 300 | PDFDemo App Delegate 301 | 302 | 303 | 20 304 | 305 | 306 | YES 307 | 308 | 309 | 310 | 311 | 312 | 27 313 | 314 | 315 | YES 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 28 324 | 325 | 326 | YES 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 29 335 | 336 | 337 | 338 | 339 | 30 340 | 341 | 342 | 343 | 344 | 39 345 | 346 | 347 | YES 348 | 349 | 350 | 351 | 352 | 353 | 38 354 | 355 | 356 | 357 | 358 | 40 359 | 360 | 361 | 362 | 363 | 43 364 | 365 | 366 | 367 | 368 | 369 | 370 | YES 371 | 372 | YES 373 | -1.CustomClassName 374 | -1.IBPluginDependency 375 | -2.CustomClassName 376 | -2.IBPluginDependency 377 | 2.IBPluginDependency 378 | 20.CustomClassName 379 | 20.IBPluginDependency 380 | 27.IBPluginDependency 381 | 28.IBPluginDependency 382 | 29.IBPluginDependency 383 | 30.CustomClassName 384 | 30.IBPluginDependency 385 | 38.IBPluginDependency 386 | 39.IBPluginDependency 387 | 40.IBPluginDependency 388 | 43.IBPluginDependency 389 | 6.CustomClassName 390 | 6.IBPluginDependency 391 | 392 | 393 | YES 394 | UIApplication 395 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 396 | UIResponder 397 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 398 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 399 | RootViewController 400 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 401 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 402 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 403 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 404 | PageView 405 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 406 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 407 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 408 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 409 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 410 | PDFKittenAppDelegate 411 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 412 | 413 | 414 | 415 | YES 416 | 417 | 418 | 419 | 420 | 421 | YES 422 | 423 | 424 | 425 | 426 | 44 427 | 428 | 429 | 430 | YES 431 | 432 | PDFKittenAppDelegate 433 | NSObject 434 | 435 | YES 436 | 437 | YES 438 | rootViewController 439 | window 440 | 441 | 442 | YES 443 | RootViewController 444 | UIWindow 445 | 446 | 447 | 448 | YES 449 | 450 | YES 451 | rootViewController 452 | window 453 | 454 | 455 | YES 456 | 457 | rootViewController 458 | RootViewController 459 | 460 | 461 | window 462 | UIWindow 463 | 464 | 465 | 466 | 467 | IBProjectSource 468 | ./Classes/PDFKittenAppDelegate.h 469 | 470 | 471 | 472 | PageView 473 | UIScrollView 474 | 475 | detailedInfoButtonPressed: 476 | id 477 | 478 | 479 | detailedInfoButtonPressed: 480 | 481 | detailedInfoButtonPressed: 482 | id 483 | 484 | 485 | 486 | dataSource 487 | id 488 | 489 | 490 | dataSource 491 | 492 | dataSource 493 | id 494 | 495 | 496 | 497 | IBProjectSource 498 | ./Classes/PageView.h 499 | 500 | 501 | 502 | RootViewController 503 | UIViewController 504 | 505 | YES 506 | 507 | YES 508 | pageView 509 | searchBar 510 | 511 | 512 | YES 513 | PageView 514 | UISearchBar 515 | 516 | 517 | 518 | YES 519 | 520 | YES 521 | pageView 522 | searchBar 523 | 524 | 525 | YES 526 | 527 | pageView 528 | PageView 529 | 530 | 531 | searchBar 532 | UISearchBar 533 | 534 | 535 | 536 | 537 | IBProjectSource 538 | ./Classes/RootViewController.h 539 | 540 | 541 | 542 | 543 | 0 544 | IBIPadFramework 545 | 546 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 547 | 548 | 549 | 550 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 551 | 552 | 553 | YES 554 | 3 555 | 933 556 | 557 | 558 | -------------------------------------------------------------------------------- /PDFKitten/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | int retVal = UIApplicationMain(argc, argv, nil, nil); 6 | return retVal; 7 | } 8 | -------------------------------------------------------------------------------- /PDFKitten/pdfScannerCallbacks.mm: -------------------------------------------------------------------------------- 1 | #import "Scanner.h" 2 | 3 | BOOL isSpace(float width, Scanner *scanner) { 4 | return abs(width) >= scanner.renderingState.font.widthOfSpace; 5 | } 6 | 7 | void didScanSpace(float value, void *info) { 8 | Scanner *scanner = (__bridge Scanner *) info; 9 | float width = [scanner.renderingState convertToUserSpace:value]; 10 | [scanner.renderingState translateTextPosition:CGSizeMake(-width, 0)]; 11 | if (isSpace(value, scanner)) { 12 | [scanner.stringDetector reset]; 13 | } 14 | } 15 | 16 | void didScanString(CGPDFStringRef pdfString, void *info) { 17 | Scanner *scanner = (__bridge Scanner *) info; 18 | StringDetector *stringDetector = scanner.stringDetector; 19 | Font *font = scanner.renderingState.font; 20 | NSString *string = [font stringWithPDFString:pdfString]; 21 | if (string) { 22 | [stringDetector appendString:string]; 23 | [scanner.content appendString:string]; 24 | } 25 | } 26 | 27 | void didScanNewLine(CGPDFScannerRef pdfScanner, Scanner *scanner, BOOL persistLeading) { 28 | CGPDFReal tx, ty; 29 | CGPDFScannerPopNumber(pdfScanner, &ty); 30 | CGPDFScannerPopNumber(pdfScanner, &tx); 31 | [scanner.renderingState newLineWithLeading:-ty indent:tx save:persistLeading]; 32 | } 33 | 34 | CGPDFStringRef getString(CGPDFScannerRef pdfScanner) { 35 | CGPDFStringRef pdfString; 36 | CGPDFScannerPopString(pdfScanner, &pdfString); 37 | return pdfString; 38 | } 39 | 40 | CGPDFReal getNumber(CGPDFScannerRef pdfScanner) { 41 | CGPDFReal value; 42 | CGPDFScannerPopNumber(pdfScanner, &value); 43 | return value; 44 | } 45 | 46 | CGPDFArrayRef getArray(CGPDFScannerRef pdfScanner) { 47 | CGPDFArrayRef pdfArray; 48 | CGPDFScannerPopArray(pdfScanner, &pdfArray); 49 | return pdfArray; 50 | } 51 | 52 | CGPDFObjectRef getObject(CGPDFArrayRef pdfArray, int index) { 53 | CGPDFObjectRef pdfObject; 54 | CGPDFArrayGetObject(pdfArray, index, &pdfObject); 55 | return pdfObject; 56 | } 57 | 58 | CGPDFStringRef getStringValue(CGPDFObjectRef pdfObject) { 59 | CGPDFStringRef string; 60 | CGPDFObjectGetValue(pdfObject, kCGPDFObjectTypeString, &string); 61 | return string; 62 | } 63 | 64 | float getNumericalValue(CGPDFObjectRef pdfObject, CGPDFObjectType type) { 65 | if (type == kCGPDFObjectTypeReal) { 66 | CGPDFReal tx; 67 | CGPDFObjectGetValue(pdfObject, kCGPDFObjectTypeReal, &tx); 68 | return tx; 69 | } 70 | else if (type == kCGPDFObjectTypeInteger) { 71 | CGPDFInteger tx; 72 | CGPDFObjectGetValue(pdfObject, kCGPDFObjectTypeInteger, &tx); 73 | return tx; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | CGAffineTransform getTransform(CGPDFScannerRef pdfScanner) { 80 | CGAffineTransform transform; 81 | transform.ty = getNumber(pdfScanner); 82 | transform.tx = getNumber(pdfScanner); 83 | transform.d = getNumber(pdfScanner); 84 | transform.c = getNumber(pdfScanner); 85 | transform.b = getNumber(pdfScanner); 86 | transform.a = getNumber(pdfScanner); 87 | return transform; 88 | } 89 | 90 | #pragma mark Text parameters 91 | 92 | void setHorizontalScale(CGPDFScannerRef pdfScanner, void *info) { 93 | Scanner *scanner = (__bridge Scanner *) info; 94 | [scanner.renderingState setHorizontalScaling:getNumber(pdfScanner)]; 95 | } 96 | 97 | void setTextLeading(CGPDFScannerRef pdfScanner, void *info) { 98 | Scanner *scanner = (__bridge Scanner *) info; 99 | [scanner.renderingState setLeadning:getNumber(pdfScanner)]; 100 | } 101 | 102 | void setFont(CGPDFScannerRef pdfScanner, void *info) { 103 | CGPDFReal fontSize; 104 | const char *fontName; 105 | CGPDFScannerPopNumber(pdfScanner, &fontSize); 106 | CGPDFScannerPopName(pdfScanner, &fontName); 107 | 108 | Scanner *scanner = (__bridge Scanner *) info; 109 | RenderingState *state = scanner.renderingState; 110 | Font *font = [scanner.fontCollection fontNamed:[NSString stringWithUTF8String:fontName]]; 111 | [state setFont:font]; 112 | [state setFontSize:fontSize]; 113 | } 114 | 115 | void setTextRise(CGPDFScannerRef pdfScanner, void *info) { 116 | Scanner *scanner = (__bridge Scanner *) info; 117 | [scanner.renderingState setTextRise:getNumber(pdfScanner)]; 118 | } 119 | 120 | void setCharacterSpacing(CGPDFScannerRef pdfScanner, void *info) { 121 | Scanner *scanner = (__bridge Scanner *) info; 122 | [scanner.renderingState setCharacterSpacing:getNumber(pdfScanner)]; 123 | } 124 | 125 | void setWordSpacing(CGPDFScannerRef pdfScanner, void *info) { 126 | Scanner *scanner = (__bridge Scanner *) info; 127 | [scanner.renderingState setWordSpacing:getNumber(pdfScanner)]; 128 | } 129 | 130 | 131 | #pragma mark Set position 132 | 133 | void newLine(CGPDFScannerRef pdfScanner, void *info) { 134 | Scanner *scanner = (__bridge Scanner *) info; 135 | [scanner.renderingState newLine]; 136 | } 137 | 138 | void newLineWithLeading(CGPDFScannerRef pdfScanner, void *info) { 139 | didScanNewLine(pdfScanner, (__bridge Scanner *) info, NO); 140 | } 141 | 142 | void newLineSetLeading(CGPDFScannerRef pdfScanner, void *info) { 143 | didScanNewLine(pdfScanner, (__bridge Scanner *) info, YES); 144 | } 145 | 146 | void newParagraph(CGPDFScannerRef pdfScanner, void *info) { 147 | Scanner *scanner = (__bridge Scanner *) info; 148 | [scanner.renderingState setTextMatrix:CGAffineTransformIdentity replaceLineMatrix:YES]; 149 | } 150 | 151 | void setTextMatrix(CGPDFScannerRef pdfScanner, void *info) { 152 | Scanner *scanner = (__bridge Scanner *) info; 153 | [scanner.renderingState setTextMatrix:getTransform(pdfScanner) replaceLineMatrix:YES]; 154 | } 155 | 156 | 157 | #pragma mark Print strings 158 | 159 | void printString(CGPDFScannerRef pdfScanner, void *info) { 160 | didScanString(getString(pdfScanner), info); 161 | } 162 | 163 | void printStringNewLine(CGPDFScannerRef scanner, void *info) { 164 | newLine(scanner, info); 165 | printString(scanner, info); 166 | } 167 | 168 | void printStringNewLineSetSpacing(CGPDFScannerRef scanner, void *info) { 169 | setWordSpacing(scanner, info); 170 | setCharacterSpacing(scanner, info); 171 | printStringNewLine(scanner, info); 172 | } 173 | 174 | void printStringsAndSpaces(CGPDFScannerRef pdfScanner, void *info) { 175 | CGPDFArrayRef array = getArray(pdfScanner); 176 | for (int i = 0; i < CGPDFArrayGetCount(array); i++) { 177 | CGPDFObjectRef pdfObject = getObject(array, i); 178 | CGPDFObjectType valueType = CGPDFObjectGetType(pdfObject); 179 | 180 | if (valueType == kCGPDFObjectTypeString) { 181 | didScanString(getStringValue(pdfObject), info); 182 | } 183 | else { 184 | didScanSpace(getNumericalValue(pdfObject, valueType), info); 185 | } 186 | } 187 | } 188 | 189 | 190 | #pragma mark Graphics state operators 191 | 192 | void pushRenderingState(CGPDFScannerRef pdfScanner, void *info) 193 | { 194 | Scanner *scanner = (__bridge Scanner *) info; 195 | RenderingState *state = [scanner.renderingState copy]; 196 | [scanner.renderingStateStack pushRenderingState:state]; 197 | } 198 | 199 | void popRenderingState(CGPDFScannerRef pdfScanner, void *info) 200 | { 201 | Scanner *scanner = (__bridge Scanner *) info; 202 | [scanner.renderingStateStack popRenderingState]; 203 | } 204 | 205 | /* Update CTM */ 206 | void applyTransformation(CGPDFScannerRef pdfScanner, void *info) 207 | { 208 | Scanner *scanner = (__bridge Scanner *) info; 209 | RenderingState *state = scanner.renderingState; 210 | state.ctm = CGAffineTransformConcat(getTransform(pdfScanner), state.ctm); 211 | } 212 | -------------------------------------------------------------------------------- /PDFKittenTests/KurtStory.txt: -------------------------------------------------------------------------------- 1 | A cat in his best years, Kurt lives alone in his countryside house, miles outside the small town of Chatmonix. In the vicinity of the grandiose French alps, Kurt has finally found a peaceful place to call home. He has found joy in life. Life has not always been good on Kurt. As a kitten, he experienced the life of a stray cat, a lost soul forever wandering in the peripherals of society. The streets were cold and dark, laden with violence and fear. With nothing left to live for, Kurt turned to darkness. Stealing and lying, begging and cheating was his way of life. As years progressed, Kurt grew older and developed a rugged street mentality and a crude attitude. Feared and respected by most, he quickly drew attention from the big shots in the world of organized crime. Starting off in the gutter dealing catnip, he was regarded as a no-nonsense cat you could trust and count on, even when things went bad. As Kurt got older, he sought the solitude of the mountains. One day he found a message on his doorstep: I am watching you. He cannot be free until he finds a way to pay them back. 2 | -------------------------------------------------------------------------------- /PDFKittenTests/PDFKittenTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.kurtcode.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PDFKittenTests/PDFKittenTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'PDFKittenTests' target in the 'PDFKittenTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /PDFKittenTests/StringDetectorTest.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "StringDetector.h" 3 | 4 | @interface StringDetectorTest : XCTestCase { 5 | int matchCount; 6 | int prefixCount; 7 | NSString *kurtStory; 8 | StringDetector *stringDetector; 9 | } 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /PDFKittenTests/StringDetectorTest.m: -------------------------------------------------------------------------------- 1 | #import "StringDetectorTest.h" 2 | 3 | #define INPUT_SEGMENT_LENGTH 10 4 | 5 | @implementation StringDetectorTest 6 | 7 | - (void)setUp { 8 | matchCount = 0; 9 | prefixCount = 0; 10 | NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"KurtStory" ofType:@"txt"]; 11 | kurtStory = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; 12 | stringDetector = [[StringDetector alloc] initWithKeyword:@"Kurt"]; 13 | [stringDetector setDelegate:self]; 14 | } 15 | 16 | - (void)appendString:(NSString *)string 17 | { 18 | int position = 0; 19 | 20 | while (position < kurtStory.length) 21 | { 22 | NSRange range = NSMakeRange(position, MIN(INPUT_SEGMENT_LENGTH, kurtStory.length - position)); 23 | [stringDetector appendString:[kurtStory substringWithRange:range]]; 24 | position = (int)NSMaxRange(range); 25 | } 26 | } 27 | 28 | - (void)testDetectStrings { 29 | [self appendString:kurtStory]; 30 | XCTAssertEqual(matchCount, 6, @"incorrect number of matches"); 31 | XCTAssertEqual(prefixCount, 11, @"incorrect number of prefixes matched"); 32 | } 33 | 34 | - (void)testIgnorePrefixes { 35 | [stringDetector appendString:@"KuKuKu"]; 36 | XCTAssertEqual(prefixCount, 3, @"incorrect number of prefixes matched"); 37 | 38 | [stringDetector appendString:@"KuKurtKurt"]; 39 | XCTAssertEqual(matchCount, 2, @"incorrect number of matches"); 40 | } 41 | 42 | - (void)testNoMatch { 43 | [stringDetector setKeyword:@"foobar"]; 44 | [self appendString:kurtStory]; 45 | XCTAssertEqual(matchCount, 0, @"matches found"); 46 | } 47 | 48 | - (void)detectorDidStartMatching:(StringDetector *)stringDetector { 49 | prefixCount++; 50 | } 51 | 52 | - (void)detectorFoundString:(StringDetector *)detector { 53 | matchCount++; 54 | } 55 | 56 | - (void)dealloc { 57 | [kurtStory release]; 58 | [super dealloc]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /PDFKittenTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kurt the PDFKitten 2 | 3 | A Proof-of-Concept framework for searching PDF documents on iOS. 4 | 5 | PLEASE NOTE THAT THIS SOFTWARE IS EXPERIMENTAL AND WILL MOST LIKELY NEVER BE FINISHED. 6 | 7 | It was created to show how PDF search and highlighting could be done in third-party apps. Alas, it will likely never reach completeness or cover all usecases. 8 | 9 | ### Why? 10 | 11 | iOS, up to and including the current fifth version, does not provide any public APIs for searching PDF documents, or determining where on a page a given word is drawn. Any developer aiming to provide these features in an app must use low-level Core Graphics APIs, and keep track of the stateful process of laying out the content of the page. 12 | 13 | This project is meant to facilitate this by implementing a complete workflow, taking as input a PDF document, a keyword string, and returning a set of selections that can be drawn on top of the PDF document. 14 | 15 | ### How? 16 | 17 | First, create a new instance of the scanner. 18 | 19 | ``` 20 | CGPDFPageRef page = CGPDFDocumentGetPage(document, 1); 21 | Scanner *scanner = [Scanner scannerWithPage:page]; 22 | ``` 23 | 24 | Set a keyword (case-insensitive) and scan a page. 25 | 26 | ``` 27 | NSArray *selections = [scanner select:@"happiness"]; 28 | ``` 29 | 30 | Finally, scan the page and draw the selections. 31 | 32 | ``` 33 | for (Selection *selection in selections) 34 | { 35 | // draw selection 36 | } 37 | ``` 38 | 39 | ### Limitations 40 | 41 | The PDF specification is huge, allowing for different fonts, text encodings et cetera. This means strict design is a must, and thorough testing is needed. At this point, this project is not fully compatible with all font types, and especially support for non-latin characters will require further development. 42 | 43 | Offering a complete solution for processing any PDF document would apparently require the inclusion of a complete library of font files. We currently do not intend to include more than the bare essentials for a proof-of-concept application. 44 | 45 | Only latin character sets are currently supported. 46 | 47 | ### License and Warranty 48 | 49 | This software is provided under the MIT license, see License.txt. 50 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | -------------------------------------------------------------------------------- /docs/PDFKitten-DeveloperManual.tex: -------------------------------------------------------------------------------- 1 | \documentclass[12pt,a4paper]{article} 2 | 3 | \parskip 1em 4 | 5 | \begin{document} 6 | 7 | 8 | \section{Class model} 9 | 10 | The framework consists of classes that mirror the relevant objects contained in a PDF document. As the main objective is to parse text, the majority of classes are concerned with modeling fonts, arranged in a hierarchy similar to the relations between different font types supported by the PDF format. 11 | 12 | \subsection{Fonts} 13 | 14 | For the purposes of this framework, a font tells us the unicode value of a character, and how much space the rendered character occupies on the page. This is how the keyword entered by the user is matched (or not) to the text in the PDF document, and the location of the word is determined. 15 | 16 | Fons can be divided into two sets -- \textit{simple} and \textit{composite} fonts. Simple fonts tend to be just that, simple, encoding characters in for example MacOSRoman or some other common encoding. Composite fonts, however, may include custom mappings to Unicode, and may even have sub-fonts that need to be consulted when reading the textual content of a document. 17 | 18 | \section{Coordinate systems} 19 | 20 | There are three coordinate systems describing textual content, device space, text space and glyph space. Glyph space describes individual glyphs (letters) within their bounding box. Coordinates in glyph space are one 1000th of a unit of font size. Text space takes into account font size, so that some text may be bigger than other. Finally, the text is translated into device space, where the entire content of the page, not only text but also pictures and other objects, may be scaled, rotated and so on. 21 | 22 | \section{Textual search} 23 | 24 | Pages are scanned one at a time, feeding the stream of text into the string detector, which uses the current font to translate characters to unicode. Following the design pattern of a finite state machine, the string detector keeps matching the stream of characters until the exact sequence of characters making up the keyword has been found, at which point a callback method informs the scanner that the keyword has been found. The string detector responds to each entered string with the string's dimension, which allows the scanner to maintain a selection object. Once the keyword is found, the current selection is finalized, stored, and a the scanner goes on looking for more occurrences of the keyword. 25 | 26 | \subsection{Selection} 27 | 28 | A selection consists of two parts; a rectangle with zero-origin and dimensions to exactly fit the keyword, and a transform placing on the page, allowing for scaling and rotation depending on how the text is rendered. 29 | 30 | \section{Information flow} 31 | Two kinds of data are extracted from each page of a document; the collection of fonts that are used on the page, and the stream of objects laying out the content of the page, out of which only the text objects and rendering transform operators are processed. Other objects, such as image objects are discarded. 32 | 33 | \subsection{} 34 | 35 | 36 | \end{document} 37 | --------------------------------------------------------------------------------