├── .gitignore
├── .swift-version
├── Assets
├── example.pdf
├── page_setup.png
├── simple_pdf.key
└── simple_pdf_logo.png
├── ExampleCode.swift
├── LICENSE
├── README.md
├── SimplePDF.podspec
├── SimplePDF.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SimplePDF
├── Info.plist
├── SimplePDF.h
└── SimplePDF.swift
└── SimplePDFTests
├── Info.plist
├── SimplePDFTests.swift
└── simple_pdf_logo.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/swift
3 |
4 | ### Swift ###
5 | # Xcode
6 | #
7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
8 |
9 | ## Build generated
10 | build/
11 | DerivedData
12 |
13 | ## Various settings
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata
23 |
24 | ## Other
25 | *.xccheckout
26 | *.moved-aside
27 | *.xcuserstate
28 | *.xcscmblueprint
29 |
30 | ## Obj-C/Swift specific
31 | *.hmap
32 | *.ipa
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
65 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/Assets/example.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nRewik/SimplePDF/35e3e7ea6500eaf9244d66b9e6d316f7b85072aa/Assets/example.pdf
--------------------------------------------------------------------------------
/Assets/page_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nRewik/SimplePDF/35e3e7ea6500eaf9244d66b9e6d316f7b85072aa/Assets/page_setup.png
--------------------------------------------------------------------------------
/Assets/simple_pdf.key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nRewik/SimplePDF/35e3e7ea6500eaf9244d66b9e6d316f7b85072aa/Assets/simple_pdf.key
--------------------------------------------------------------------------------
/Assets/simple_pdf_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nRewik/SimplePDF/35e3e7ea6500eaf9244d66b9e6d316f7b85072aa/Assets/simple_pdf_logo.png
--------------------------------------------------------------------------------
/ExampleCode.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SimplePDF
3 |
4 | let a4PaperSize = CGSize(width: 595, height: 842)
5 | let pdf = SimplePDF(pageSize: a4PaperSize)
6 |
7 | pdf.setContentAlignment(.Center)
8 |
9 | // add logo image
10 | let logoImage = UIImage(named:"simple_pdf_logo")!
11 | pdf.addImage(logoImage)
12 |
13 | pdf.addLineSpace(30)
14 |
15 | pdf.setContentAlignment(.Left)
16 | pdf.addText("Normal text follows by line separator")
17 | pdf.addLineSeparator()
18 |
19 | pdf.addLineSpace(20.0)
20 |
21 | pdf.setContentAlignment(.Right)
22 | pdf.addText("Text after set content alignment to .Right")
23 | pdf.addLineSpace(20.0)
24 |
25 | pdf.addText("Cras quis eros orci.\nLorem ipsum dolor sit amet, consectetur adipiscing elit.\nDonec mollis vitae mi ut lobortis.\nUt ultrices mi vel neque venenatis, ut efficitur metus eleifend. Sed pellentesque lobortis est quis maximus. Maecenas ultricies risus et enim consectetur, id lobortis ante porta. Quisque at euismod enim. Vestibulum faucibus purus non justo fringilla, sit amet iaculis ex pellentesque. Vestibulum eget vulputate diam, sit amet ornare sem. Duis at eros non tortor malesuada accumsan.\nNunc vel libero ut sapien dictum iaculis a vel odio. Quisque purus turpis, tristique auctor ex non, consectetur scelerisque lorem. Mauris est justo, sollicitudin sit amet nisi a, mattis posuere orci. Sed elementum est at est tristique gravida. Aliquam iaculis, metus facilisis varius viverra, nunc libero ultricies arcu, in accumsan sem nibh vel purus.")
26 |
27 | pdf.addLineSpace(30)
28 |
29 | pdf.setContentAlignment(.Center)
30 |
31 | pdf.addText("Center Text")
32 | pdf.addLineSpace(20.0)
33 | pdf.addText("Cras varius leo ac lectus malesuada, ut rhoncus nunc blandit.\n In venenatis diam et vehicula suscipit.\n Aliquam in ante at dolor sodales consectetur ut semper quam.\n Vivamus massa purus, congue sed leo sed, lobortis consectetur lacus. Nunc sed tortor nec augue mattis faucibus. Sed malesuada metus in sapien efficitur, ut aliquet nisl lobortis. Vestibulum faucibus purus non justo fringilla, sit amet iaculis ex pellentesque. Vestibulum eget vulputate diam, sit amet ornare sem. Aliquam erat volutpat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin scelerisque posuere mi, non consequat mauris auctor a. Fusce lacinia auctor lectus a elementum.")
34 |
35 |
36 | pdf.addLineSpace(30.0)
37 |
38 | pdf.setContentAlignment(.Left)
39 | let textString = "This is an example of long text. If the text doesn't fit in the current page. Simple pdf will draw a part of text, and automatically begin a new page to draw the remaining text. This process will be repeated until there's no text left to draw. "
40 | pdf.addText(textString)
41 |
42 |
43 | pdf.beginNewPage()
44 | pdf.addText("Begin new page")
45 |
46 | // Generate PDF data and save to a local file.
47 | if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
48 |
49 | let fileName = "example.pdf"
50 | let documentsFileName = documentDirectories + "/" + fileName
51 |
52 | let pdfData = pdf.generatePDFdata()
53 | do{
54 | try pdfData.writeToFile(documentsFileName, options: .DataWritingAtomic)
55 | print("\nThe generated pdf can be found at:")
56 | print("\n\t\(documentsFileName)\n")
57 | }catch{
58 | print(error)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Nutchaphon Rewik
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | SimplePDF is a wrapper of UIGraphics PDF context written in Swift.
15 |
16 | You can:
17 | - [x] add texts, images, spaces and lines, table
18 | - [x] set up page layout, adjust content alignment
19 | - [x] generate PDF data/file.
20 |
21 | In summary, you can create a simple PDF effortlessly. :smile:
22 |
23 | ## Example
24 |
25 | ```swift
26 | let A4paperSize = CGSize(width: 595, height: 842)
27 | let pdf = SimplePDF(pageSize: A4paperSize)
28 |
29 | pdf.addText("Hello World!")
30 | // or
31 | // pdf.addText("Hello World!", font: myFont, textColor: myTextColor)
32 |
33 | pdf.addImage( anImage )
34 |
35 | let dataArray = [["Test1", "Test2"],["Test3", "Test4"]]
36 | pdf.addTable(rowCount: 2, columnCount: 2, rowHeight: 20.0, columnWidth: 30.0, tableLineWidth: 1.0, font: UIFont.systemFontOfSize(5.0), dataArray: dataArray)
37 |
38 | let pdfData = pdf.generatePDFdata()
39 |
40 | // save as a local file
41 | try? pdfData.writeToFile(path, options: .DataWritingAtomic)
42 | ```
43 |
44 | See the result [example.pdf](Assets/example.pdf), which is generated from [ExampleCode.swift](ExampleCode.swift).
45 |
46 | ## Installation
47 |
48 | via [Cocoapods](https://cocoapods.org)
49 |
50 | ```ruby
51 | use_frameworks!
52 | pod 'SimplePDF'
53 | ```
54 |
55 | ## Usage
56 |
57 | ```swift
58 | import SimplePDF
59 | ```
60 |
61 | ### Page Setup
62 |
63 | 
64 |
65 | ```swift
66 | let A4paperSize = CGSize(width: 595, height: 842)
67 | let pdf = SimplePDF(pageSize: A4paperSize, pageMargin: 20.0)
68 | // or define all margins extra
69 | let pdf = SimplePDF(pageSize: A4paperSize, pageMarginLeft: 35, pageMarginTop: 50, pageMarginBottom: 40, pageMarginRight: 35)
70 | ```
71 |
72 | ### Write Something
73 |
74 | ```swift
75 | pdf.addText( "some text" )
76 | pdf.addImage( UIImage )
77 | pdf.addAttributedText( NSAttributedString )
78 | pdf.addLineSeparator(height: 30) // or pdf.addLineSeparator() default height is 1.0
79 | pdf.addLineSpace(20)
80 | ```
81 |
82 | ### Layout
83 | You can layout horizontally and vertically
84 | ```swift
85 | // Start a horizonal arrangement
86 | pdf.beginHorizontalArrangement()
87 | // Add space from the left
88 | pdf.addHorizontalSpace(60)
89 |
90 | // now add your text, table, image, ...
91 |
92 | // finishe the horizontal arrangement so you can continue vertically
93 | pdf.endHorizontalArrangement()
94 |
95 | // adds a vertical space
96 | pdf.addVerticalSpace(70)
97 | ```
98 |
99 | ### Table Definitions
100 | Define the layout of tables with definitions
101 | ```swift
102 | let tableDef = TableDefinition(alignments: [.left, .left],
103 | columnWidths: [100, 300],
104 | fonts: [UIFont.systemFont(ofSize: 20),
105 | UIFont.systemFont(ofSize: 16)],
106 | textColors: [UIColor.black,
107 | UIColor.blue])
108 |
109 | let data = [] // my data
110 |
111 | pdf.addTable(data.count,
112 | columnCount: 2,
113 | rowHeight: 25,
114 | tableLineWidth: 0, // this is taken from the definition
115 | tableDefinition: tableDef,
116 | dataArray: data)
117 | ```
118 |
119 | ### Utilities
120 |
121 | ```swift
122 | pdf.beginNewPage() // Begin a new page
123 | ```
124 |
125 | These following commands will affect everything you write after you call.
126 |
127 | ```swift
128 | pdf.setContentAlignment(.Center) // .Left, .Center, .Right
129 | ```
130 |
131 | ### Generate PDF data
132 |
133 | ```swift
134 | let pdfData = pdf.generatePDFdata()
135 |
136 | // write to file
137 | try? pdfData.writeToFile(path, options: .DataWritingAtomic)
138 |
139 | // or upload to Internet
140 | // For example, Alamofire.upload(...)
141 | ```
142 |
143 | ## License
144 | SimplePDF is available under the [MIT License](LICENSE).
145 |
146 | ## Authors
147 | Nutchaphon Rewik
148 |
149 | [![my twitter][1.1]][1]
150 |
151 | [1.1]: https://img.shields.io/badge/Twitter-@nRewik-blue.svg?style=flat-square
152 | [1]: https://www.twitter.com/nRewik
153 |
--------------------------------------------------------------------------------
/SimplePDF.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 |
3 | spec.name = "SimplePDF"
4 | spec.version = "3.0.0"
5 | spec.summary = "A library for creating simple pdf files."
6 | spec.homepage = "https://github.com/nrewik/SimplePDF"
7 | spec.license = { type: 'MIT', file: 'LICENSE' }
8 | spec.authors = { "Nutchaphon Rewik" => 'nrewik@outlook.com' }
9 | spec.social_media_url = "http://twitter.com/nrewik"
10 |
11 | spec.platform = :ios, "8.0"
12 | spec.requires_arc = true
13 | spec.source = { git: "https://github.com/nrewik/SimplePDF.git", tag: "v#{spec.version}", submodules: true }
14 | spec.source_files = "SimplePDF/**/*.{h,swift}"
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/SimplePDF.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A07943FC1C4B5E5F000ADEFC /* SimplePDF.h in Headers */ = {isa = PBXBuildFile; fileRef = A07943FB1C4B5E5F000ADEFC /* SimplePDF.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | A07944031C4B5E5F000ADEFC /* SimplePDF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07943F81C4B5E5F000ADEFC /* SimplePDF.framework */; };
12 | A07944131C4B5EE4000ADEFC /* SimplePDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07944121C4B5EE4000ADEFC /* SimplePDF.swift */; };
13 | A07944171C4B5EFE000ADEFC /* simple_pdf_logo.png in Resources */ = {isa = PBXBuildFile; fileRef = A07944151C4B5EFE000ADEFC /* simple_pdf_logo.png */; };
14 | A079441C1C4B5F6C000ADEFC /* SimplePDFTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A079441B1C4B5F6C000ADEFC /* SimplePDFTests.swift */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXContainerItemProxy section */
18 | A07944041C4B5E5F000ADEFC /* PBXContainerItemProxy */ = {
19 | isa = PBXContainerItemProxy;
20 | containerPortal = A07943EF1C4B5E5F000ADEFC /* Project object */;
21 | proxyType = 1;
22 | remoteGlobalIDString = A07943F71C4B5E5F000ADEFC;
23 | remoteInfo = SimplePDF;
24 | };
25 | /* End PBXContainerItemProxy section */
26 |
27 | /* Begin PBXFileReference section */
28 | A07943F81C4B5E5F000ADEFC /* SimplePDF.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SimplePDF.framework; sourceTree = BUILT_PRODUCTS_DIR; };
29 | A07943FB1C4B5E5F000ADEFC /* SimplePDF.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimplePDF.h; sourceTree = ""; };
30 | A07943FD1C4B5E5F000ADEFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | A07944021C4B5E5F000ADEFC /* SimplePDFTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimplePDFTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
32 | A07944091C4B5E5F000ADEFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | A07944121C4B5EE4000ADEFC /* SimplePDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimplePDF.swift; sourceTree = ""; };
34 | A07944151C4B5EFE000ADEFC /* simple_pdf_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = simple_pdf_logo.png; sourceTree = ""; };
35 | A079441B1C4B5F6C000ADEFC /* SimplePDFTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimplePDFTests.swift; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | A07943F41C4B5E5F000ADEFC /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | A07943FF1C4B5E5F000ADEFC /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | A07944031C4B5E5F000ADEFC /* SimplePDF.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | A07943EE1C4B5E5F000ADEFC = {
58 | isa = PBXGroup;
59 | children = (
60 | A07943FA1C4B5E5F000ADEFC /* SimplePDF */,
61 | A07944061C4B5E5F000ADEFC /* SimplePDFTests */,
62 | A07943F91C4B5E5F000ADEFC /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | A07943F91C4B5E5F000ADEFC /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | A07943F81C4B5E5F000ADEFC /* SimplePDF.framework */,
70 | A07944021C4B5E5F000ADEFC /* SimplePDFTests.xctest */,
71 | );
72 | name = Products;
73 | sourceTree = "";
74 | };
75 | A07943FA1C4B5E5F000ADEFC /* SimplePDF */ = {
76 | isa = PBXGroup;
77 | children = (
78 | A07943FB1C4B5E5F000ADEFC /* SimplePDF.h */,
79 | A07944121C4B5EE4000ADEFC /* SimplePDF.swift */,
80 | A07943FD1C4B5E5F000ADEFC /* Info.plist */,
81 | );
82 | path = SimplePDF;
83 | sourceTree = "";
84 | };
85 | A07944061C4B5E5F000ADEFC /* SimplePDFTests */ = {
86 | isa = PBXGroup;
87 | children = (
88 | A079441B1C4B5F6C000ADEFC /* SimplePDFTests.swift */,
89 | A07944151C4B5EFE000ADEFC /* simple_pdf_logo.png */,
90 | A07944091C4B5E5F000ADEFC /* Info.plist */,
91 | );
92 | path = SimplePDFTests;
93 | sourceTree = "";
94 | };
95 | /* End PBXGroup section */
96 |
97 | /* Begin PBXHeadersBuildPhase section */
98 | A07943F51C4B5E5F000ADEFC /* Headers */ = {
99 | isa = PBXHeadersBuildPhase;
100 | buildActionMask = 2147483647;
101 | files = (
102 | A07943FC1C4B5E5F000ADEFC /* SimplePDF.h in Headers */,
103 | );
104 | runOnlyForDeploymentPostprocessing = 0;
105 | };
106 | /* End PBXHeadersBuildPhase section */
107 |
108 | /* Begin PBXNativeTarget section */
109 | A07943F71C4B5E5F000ADEFC /* SimplePDF */ = {
110 | isa = PBXNativeTarget;
111 | buildConfigurationList = A079440C1C4B5E5F000ADEFC /* Build configuration list for PBXNativeTarget "SimplePDF" */;
112 | buildPhases = (
113 | A07943F31C4B5E5F000ADEFC /* Sources */,
114 | A07943F41C4B5E5F000ADEFC /* Frameworks */,
115 | A07943F51C4B5E5F000ADEFC /* Headers */,
116 | A07943F61C4B5E5F000ADEFC /* Resources */,
117 | );
118 | buildRules = (
119 | );
120 | dependencies = (
121 | );
122 | name = SimplePDF;
123 | productName = SimplePDF;
124 | productReference = A07943F81C4B5E5F000ADEFC /* SimplePDF.framework */;
125 | productType = "com.apple.product-type.framework";
126 | };
127 | A07944011C4B5E5F000ADEFC /* SimplePDFTests */ = {
128 | isa = PBXNativeTarget;
129 | buildConfigurationList = A079440F1C4B5E5F000ADEFC /* Build configuration list for PBXNativeTarget "SimplePDFTests" */;
130 | buildPhases = (
131 | A07943FE1C4B5E5F000ADEFC /* Sources */,
132 | A07943FF1C4B5E5F000ADEFC /* Frameworks */,
133 | A07944001C4B5E5F000ADEFC /* Resources */,
134 | );
135 | buildRules = (
136 | );
137 | dependencies = (
138 | A07944051C4B5E5F000ADEFC /* PBXTargetDependency */,
139 | );
140 | name = SimplePDFTests;
141 | productName = SimplePDFTests;
142 | productReference = A07944021C4B5E5F000ADEFC /* SimplePDFTests.xctest */;
143 | productType = "com.apple.product-type.bundle.unit-test";
144 | };
145 | /* End PBXNativeTarget section */
146 |
147 | /* Begin PBXProject section */
148 | A07943EF1C4B5E5F000ADEFC /* Project object */ = {
149 | isa = PBXProject;
150 | attributes = {
151 | LastSwiftUpdateCheck = 0720;
152 | LastUpgradeCheck = 0900;
153 | ORGANIZATIONNAME = "Nutchaphon Rewik";
154 | TargetAttributes = {
155 | A07943F71C4B5E5F000ADEFC = {
156 | CreatedOnToolsVersion = 7.2;
157 | LastSwiftMigration = 0900;
158 | };
159 | A07944011C4B5E5F000ADEFC = {
160 | CreatedOnToolsVersion = 7.2;
161 | LastSwiftMigration = 0900;
162 | };
163 | };
164 | };
165 | buildConfigurationList = A07943F21C4B5E5F000ADEFC /* Build configuration list for PBXProject "SimplePDF" */;
166 | compatibilityVersion = "Xcode 3.2";
167 | developmentRegion = English;
168 | hasScannedForEncodings = 0;
169 | knownRegions = (
170 | en,
171 | );
172 | mainGroup = A07943EE1C4B5E5F000ADEFC;
173 | productRefGroup = A07943F91C4B5E5F000ADEFC /* Products */;
174 | projectDirPath = "";
175 | projectRoot = "";
176 | targets = (
177 | A07943F71C4B5E5F000ADEFC /* SimplePDF */,
178 | A07944011C4B5E5F000ADEFC /* SimplePDFTests */,
179 | );
180 | };
181 | /* End PBXProject section */
182 |
183 | /* Begin PBXResourcesBuildPhase section */
184 | A07943F61C4B5E5F000ADEFC /* Resources */ = {
185 | isa = PBXResourcesBuildPhase;
186 | buildActionMask = 2147483647;
187 | files = (
188 | );
189 | runOnlyForDeploymentPostprocessing = 0;
190 | };
191 | A07944001C4B5E5F000ADEFC /* Resources */ = {
192 | isa = PBXResourcesBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | A07944171C4B5EFE000ADEFC /* simple_pdf_logo.png in Resources */,
196 | );
197 | runOnlyForDeploymentPostprocessing = 0;
198 | };
199 | /* End PBXResourcesBuildPhase section */
200 |
201 | /* Begin PBXSourcesBuildPhase section */
202 | A07943F31C4B5E5F000ADEFC /* Sources */ = {
203 | isa = PBXSourcesBuildPhase;
204 | buildActionMask = 2147483647;
205 | files = (
206 | A07944131C4B5EE4000ADEFC /* SimplePDF.swift in Sources */,
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | };
210 | A07943FE1C4B5E5F000ADEFC /* Sources */ = {
211 | isa = PBXSourcesBuildPhase;
212 | buildActionMask = 2147483647;
213 | files = (
214 | A079441C1C4B5F6C000ADEFC /* SimplePDFTests.swift in Sources */,
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | };
218 | /* End PBXSourcesBuildPhase section */
219 |
220 | /* Begin PBXTargetDependency section */
221 | A07944051C4B5E5F000ADEFC /* PBXTargetDependency */ = {
222 | isa = PBXTargetDependency;
223 | target = A07943F71C4B5E5F000ADEFC /* SimplePDF */;
224 | targetProxy = A07944041C4B5E5F000ADEFC /* PBXContainerItemProxy */;
225 | };
226 | /* End PBXTargetDependency section */
227 |
228 | /* Begin XCBuildConfiguration section */
229 | A079440A1C4B5E5F000ADEFC /* Debug */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
234 | CLANG_CXX_LIBRARY = "libc++";
235 | CLANG_ENABLE_MODULES = YES;
236 | CLANG_ENABLE_OBJC_ARC = YES;
237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
238 | CLANG_WARN_BOOL_CONVERSION = YES;
239 | CLANG_WARN_COMMA = YES;
240 | CLANG_WARN_CONSTANT_CONVERSION = YES;
241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
242 | CLANG_WARN_EMPTY_BODY = YES;
243 | CLANG_WARN_ENUM_CONVERSION = YES;
244 | CLANG_WARN_INFINITE_RECURSION = YES;
245 | CLANG_WARN_INT_CONVERSION = YES;
246 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
247 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
248 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
249 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
250 | CLANG_WARN_STRICT_PROTOTYPES = YES;
251 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
252 | CLANG_WARN_UNREACHABLE_CODE = YES;
253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
254 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
255 | COPY_PHASE_STRIP = NO;
256 | CURRENT_PROJECT_VERSION = 1;
257 | DEBUG_INFORMATION_FORMAT = dwarf;
258 | ENABLE_STRICT_OBJC_MSGSEND = YES;
259 | ENABLE_TESTABILITY = YES;
260 | GCC_C_LANGUAGE_STANDARD = gnu99;
261 | GCC_DYNAMIC_NO_PIC = NO;
262 | GCC_NO_COMMON_BLOCKS = YES;
263 | GCC_OPTIMIZATION_LEVEL = 0;
264 | GCC_PREPROCESSOR_DEFINITIONS = (
265 | "DEBUG=1",
266 | "$(inherited)",
267 | );
268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
270 | GCC_WARN_UNDECLARED_SELECTOR = YES;
271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
272 | GCC_WARN_UNUSED_FUNCTION = YES;
273 | GCC_WARN_UNUSED_VARIABLE = YES;
274 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
275 | MTL_ENABLE_DEBUG_INFO = YES;
276 | ONLY_ACTIVE_ARCH = YES;
277 | SDKROOT = iphoneos;
278 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
279 | SWIFT_VERSION = 4.0;
280 | TARGETED_DEVICE_FAMILY = "1,2";
281 | VERSIONING_SYSTEM = "apple-generic";
282 | VERSION_INFO_PREFIX = "";
283 | };
284 | name = Debug;
285 | };
286 | A079440B1C4B5E5F000ADEFC /* Release */ = {
287 | isa = XCBuildConfiguration;
288 | buildSettings = {
289 | ALWAYS_SEARCH_USER_PATHS = NO;
290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
291 | CLANG_CXX_LIBRARY = "libc++";
292 | CLANG_ENABLE_MODULES = YES;
293 | CLANG_ENABLE_OBJC_ARC = YES;
294 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
295 | CLANG_WARN_BOOL_CONVERSION = YES;
296 | CLANG_WARN_COMMA = YES;
297 | CLANG_WARN_CONSTANT_CONVERSION = YES;
298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
299 | CLANG_WARN_EMPTY_BODY = YES;
300 | CLANG_WARN_ENUM_CONVERSION = YES;
301 | CLANG_WARN_INFINITE_RECURSION = YES;
302 | CLANG_WARN_INT_CONVERSION = YES;
303 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
304 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
305 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
307 | CLANG_WARN_STRICT_PROTOTYPES = YES;
308 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
309 | CLANG_WARN_UNREACHABLE_CODE = YES;
310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
311 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
312 | COPY_PHASE_STRIP = NO;
313 | CURRENT_PROJECT_VERSION = 1;
314 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
315 | ENABLE_NS_ASSERTIONS = NO;
316 | ENABLE_STRICT_OBJC_MSGSEND = YES;
317 | GCC_C_LANGUAGE_STANDARD = gnu99;
318 | GCC_NO_COMMON_BLOCKS = YES;
319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
321 | GCC_WARN_UNDECLARED_SELECTOR = YES;
322 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
323 | GCC_WARN_UNUSED_FUNCTION = YES;
324 | GCC_WARN_UNUSED_VARIABLE = YES;
325 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
326 | MTL_ENABLE_DEBUG_INFO = NO;
327 | SDKROOT = iphoneos;
328 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
329 | SWIFT_VERSION = 4.0;
330 | TARGETED_DEVICE_FAMILY = "1,2";
331 | VALIDATE_PRODUCT = YES;
332 | VERSIONING_SYSTEM = "apple-generic";
333 | VERSION_INFO_PREFIX = "";
334 | };
335 | name = Release;
336 | };
337 | A079440D1C4B5E5F000ADEFC /* Debug */ = {
338 | isa = XCBuildConfiguration;
339 | buildSettings = {
340 | CLANG_ENABLE_MODULES = YES;
341 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
342 | DEFINES_MODULE = YES;
343 | DYLIB_COMPATIBILITY_VERSION = 1;
344 | DYLIB_CURRENT_VERSION = 1;
345 | DYLIB_INSTALL_NAME_BASE = "@rpath";
346 | INFOPLIST_FILE = SimplePDF/Info.plist;
347 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
348 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
349 | PRODUCT_BUNDLE_IDENTIFIER = io.github.nrewik.SimplePDF;
350 | PRODUCT_NAME = "$(TARGET_NAME)";
351 | SKIP_INSTALL = YES;
352 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
353 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
354 | SWIFT_VERSION = 4.2;
355 | };
356 | name = Debug;
357 | };
358 | A079440E1C4B5E5F000ADEFC /* Release */ = {
359 | isa = XCBuildConfiguration;
360 | buildSettings = {
361 | CLANG_ENABLE_MODULES = YES;
362 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
363 | DEFINES_MODULE = YES;
364 | DYLIB_COMPATIBILITY_VERSION = 1;
365 | DYLIB_CURRENT_VERSION = 1;
366 | DYLIB_INSTALL_NAME_BASE = "@rpath";
367 | INFOPLIST_FILE = SimplePDF/Info.plist;
368 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
369 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
370 | PRODUCT_BUNDLE_IDENTIFIER = io.github.nrewik.SimplePDF;
371 | PRODUCT_NAME = "$(TARGET_NAME)";
372 | SKIP_INSTALL = YES;
373 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
374 | SWIFT_VERSION = 4.2;
375 | };
376 | name = Release;
377 | };
378 | A07944101C4B5E5F000ADEFC /* Debug */ = {
379 | isa = XCBuildConfiguration;
380 | buildSettings = {
381 | CLANG_ENABLE_MODULES = YES;
382 | INFOPLIST_FILE = SimplePDFTests/Info.plist;
383 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
384 | PRODUCT_BUNDLE_IDENTIFIER = io.github.nrewik.SimplePDFTests;
385 | PRODUCT_NAME = "$(TARGET_NAME)";
386 | SWIFT_OBJC_BRIDGING_HEADER = "";
387 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
388 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
389 | SWIFT_VERSION = 4.0;
390 | };
391 | name = Debug;
392 | };
393 | A07944111C4B5E5F000ADEFC /* Release */ = {
394 | isa = XCBuildConfiguration;
395 | buildSettings = {
396 | CLANG_ENABLE_MODULES = YES;
397 | INFOPLIST_FILE = SimplePDFTests/Info.plist;
398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
399 | PRODUCT_BUNDLE_IDENTIFIER = io.github.nrewik.SimplePDFTests;
400 | PRODUCT_NAME = "$(TARGET_NAME)";
401 | SWIFT_OBJC_BRIDGING_HEADER = "";
402 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
403 | SWIFT_VERSION = 4.0;
404 | };
405 | name = Release;
406 | };
407 | /* End XCBuildConfiguration section */
408 |
409 | /* Begin XCConfigurationList section */
410 | A07943F21C4B5E5F000ADEFC /* Build configuration list for PBXProject "SimplePDF" */ = {
411 | isa = XCConfigurationList;
412 | buildConfigurations = (
413 | A079440A1C4B5E5F000ADEFC /* Debug */,
414 | A079440B1C4B5E5F000ADEFC /* Release */,
415 | );
416 | defaultConfigurationIsVisible = 0;
417 | defaultConfigurationName = Release;
418 | };
419 | A079440C1C4B5E5F000ADEFC /* Build configuration list for PBXNativeTarget "SimplePDF" */ = {
420 | isa = XCConfigurationList;
421 | buildConfigurations = (
422 | A079440D1C4B5E5F000ADEFC /* Debug */,
423 | A079440E1C4B5E5F000ADEFC /* Release */,
424 | );
425 | defaultConfigurationIsVisible = 0;
426 | defaultConfigurationName = Release;
427 | };
428 | A079440F1C4B5E5F000ADEFC /* Build configuration list for PBXNativeTarget "SimplePDFTests" */ = {
429 | isa = XCConfigurationList;
430 | buildConfigurations = (
431 | A07944101C4B5E5F000ADEFC /* Debug */,
432 | A07944111C4B5E5F000ADEFC /* Release */,
433 | );
434 | defaultConfigurationIsVisible = 0;
435 | defaultConfigurationName = Release;
436 | };
437 | /* End XCConfigurationList section */
438 | };
439 | rootObject = A07943EF1C4B5E5F000ADEFC /* Project object */;
440 | }
441 |
--------------------------------------------------------------------------------
/SimplePDF.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SimplePDF/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SimplePDF/SimplePDF.h:
--------------------------------------------------------------------------------
1 | //
2 | // SimplePDF.h
3 | // SimplePDF
4 | //
5 | // Created by Nutchaphon Rewik on 17/01/2016.
6 | // Copyright © 2016 Nutchaphon Rewik. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SimplePDF.
12 | FOUNDATION_EXPORT double SimplePDFVersionNumber;
13 |
14 | //! Project version string for SimplePDF.
15 | FOUNDATION_EXPORT const unsigned char SimplePDFVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/SimplePDF/SimplePDF.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimplePDF.swift
3 | // SimplePDF
4 | //
5 | // Created by Nutchaphon Rewik on 13/01/2016.
6 | // Copyright © 2016 Nutchaphon Rewik. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private enum SimplePDFCommand {
12 |
13 | case addText(text:String, font:UIFont, textColor:UIColor)
14 | case addAttributedText( NSAttributedString )
15 | case addImage(UIImage)
16 | case addLineSpace(CGFloat)
17 | case addHorizontalSpace(CGFloat)
18 | case addLineSeparator(height: CGFloat)
19 | case addTable(rowCount: Int, columnCount: Int, rowHeight: CGFloat, columnWidth: CGFloat?, tableLineWidth: CGFloat, font: UIFont?, tableDefinition:TableDefinition?, dataArray: Array>)
20 |
21 | case setContentAlignment(ContentAlignment)
22 | case beginNewPage
23 |
24 | case beginHorizontalArrangement
25 | case endHorizontalArrangement
26 |
27 | }
28 |
29 | public enum ContentAlignment {
30 | case left, center, right
31 | }
32 |
33 | public struct TableDefinition {
34 | let alignments: [ContentAlignment]
35 | let columnWidths: [CGFloat]
36 | let fonts:[UIFont]
37 | let textColors:[UIColor]
38 |
39 | public init(alignments: [ContentAlignment],
40 | columnWidths: [CGFloat],
41 | fonts:[UIFont],
42 | textColors:[UIColor]) {
43 | self.alignments = alignments
44 | self.columnWidths = columnWidths
45 | self.fonts = fonts
46 | self.textColors = textColors
47 | }
48 | }
49 |
50 | open class SimplePDF {
51 |
52 | /* States */
53 | fileprivate var commands: [SimplePDFCommand] = []
54 |
55 | /* Initialization */
56 | fileprivate let pageBounds: CGRect
57 | fileprivate let pageMarginLeft: CGFloat
58 | fileprivate let pageMarginTop: CGFloat
59 | fileprivate let pageMarginBottom: CGFloat
60 | fileprivate let pageMarginRight: CGFloat
61 |
62 | public init(pageSize: CGSize, pageMargin: CGFloat = 20.0) {
63 | pageBounds = CGRect(origin: CGPoint.zero, size: pageSize)
64 | self.pageMarginLeft = pageMargin
65 | self.pageMarginTop = pageMargin
66 | self.pageMarginRight = pageMargin
67 | self.pageMarginBottom = pageMargin
68 | }
69 |
70 | public init(pageSize: CGSize, pageMarginLeft: CGFloat, pageMarginTop: CGFloat, pageMarginBottom: CGFloat, pageMarginRight: CGFloat) {
71 | pageBounds = CGRect(origin: CGPoint.zero, size: pageSize)
72 | self.pageMarginBottom = pageMarginBottom
73 | self.pageMarginRight = pageMarginRight
74 | self.pageMarginTop = pageMarginTop
75 | self.pageMarginLeft = pageMarginLeft
76 | }
77 |
78 |
79 | /// Text will be drawn from the current font and alignment settings.
80 | ///
81 | /// If text is too long and doesn't fit in the current page.
82 | /// SimplePDF will begin a new page and draw remaining text.
83 | ///
84 | /// This process will be repeated untill there's no text left to draw.
85 | open func addText(_ text: String, font:UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize), textColor:UIColor = UIColor.black) {
86 | commands += [ .addText(text: text, font: font, textColor: textColor) ]
87 | }
88 |
89 |
90 | /// - Important: Font and Content alignment settings will be ignored.
91 | /// You have to manually add those attributes to attributed text yourself.
92 | open func addAttributedText( _ attributedText: NSAttributedString) {
93 | commands += [ .addAttributedText(attributedText) ]
94 | }
95 |
96 | open func addImage(_ image: UIImage) {
97 | commands += [ .addImage(image) ]
98 | }
99 |
100 | open func addLineSpace(_ space: CGFloat) {
101 | commands += [ .addLineSpace(space) ]
102 | }
103 |
104 | open func addVerticalSpace(_ space:CGFloat) {
105 | commands += [ .addLineSpace(space) ]
106 | }
107 |
108 | open func addHorizontalSpace(_ space: CGFloat) {
109 | commands += [ .addHorizontalSpace(space) ]
110 | }
111 |
112 | open func addLineSeparator(height: CGFloat = 1.0) {
113 | commands += [ .addLineSeparator(height: height) ]
114 | }
115 |
116 | open func addTable(_ rowCount: Int, columnCount: Int, rowHeight: CGFloat, columnWidth: CGFloat, tableLineWidth: CGFloat, font: UIFont, dataArray: Array>) {
117 | commands += [ .addTable(rowCount: rowCount, columnCount: columnCount, rowHeight: rowHeight, columnWidth: columnWidth, tableLineWidth: tableLineWidth, font: font, tableDefinition: nil, dataArray: dataArray) ]
118 | }
119 |
120 | open func addTable(_ rowCount: Int, columnCount: Int, rowHeight: CGFloat, tableLineWidth: CGFloat, tableDefinition: TableDefinition, dataArray: Array>) {
121 | commands += [ .addTable(rowCount: rowCount, columnCount: columnCount, rowHeight: rowHeight, columnWidth: nil, tableLineWidth: tableLineWidth, font: nil, tableDefinition: tableDefinition, dataArray: dataArray) ]
122 | }
123 |
124 | open func setContentAlignment(_ alignment: ContentAlignment) {
125 | commands += [ .setContentAlignment(alignment) ]
126 | }
127 |
128 | open func beginNewPage() {
129 | commands += [ .beginNewPage ]
130 | }
131 |
132 | open func beginHorizontalArrangement() {
133 | commands += [ .beginHorizontalArrangement ]
134 | }
135 |
136 | open func endHorizontalArrangement() {
137 | commands += [ .endHorizontalArrangement ]
138 | }
139 |
140 | /// - returns: drawing text rect
141 | fileprivate func drawText(_ text: String, font: UIFont, textColor: UIColor, alignment: ContentAlignment, currentOffset: CGPoint) -> CGRect {
142 |
143 | // Draw attributed text from font and paragraph style attribute.
144 |
145 | let paragraphStyle = NSMutableParagraphStyle()
146 | switch alignment {
147 | case .left:
148 | paragraphStyle.alignment = .left
149 | case .center:
150 | paragraphStyle.alignment = .center
151 | case .right:
152 | paragraphStyle.alignment = .right
153 | }
154 |
155 | let attributes: [NSAttributedString.Key: Any] = [
156 | .font: font,
157 | .foregroundColor: textColor,
158 | .paragraphStyle: paragraphStyle
159 | ]
160 | let attributedText = NSAttributedString(string: text, attributes: attributes)
161 |
162 | return drawAttributedText(attributedText, currentOffset: currentOffset)
163 | }
164 |
165 | fileprivate func drawAttributedText( _ attributedText: NSAttributedString, currentOffset: CGPoint) -> CGRect {
166 |
167 | var drawingYoffset = currentOffset.y
168 |
169 | let currentText = CFAttributedStringCreateCopy(nil, attributedText as CFAttributedString)
170 | let framesetter = CTFramesetterCreateWithAttributedString(currentText!)
171 | var currentRange = CFRange(location: 0, length: 0)
172 | var done = false
173 |
174 | var lastDrawnFrame: CGRect!
175 |
176 | repeat {
177 |
178 | // Get the graphics context.
179 | let currentContext = UIGraphicsGetCurrentContext()!
180 |
181 | // Push state
182 | currentContext.saveGState()
183 |
184 | // Put the text matrix into a known state. This ensures
185 | // that no old scaling factors are left in place.
186 | currentContext.textMatrix = CGAffineTransform.identity
187 |
188 | // print("y offset: \t\(drawingYOffset)")
189 |
190 | let textMaxWidth = pageBounds.width - pageMarginLeft - pageMarginRight - currentOffset.x
191 | let textMaxHeight = pageBounds.height - pageMarginBottom - drawingYoffset
192 |
193 | // print("drawing y offset: \t\(drawingYOffset)")
194 | // print("text max height: \t\(textMaxHeight)")
195 |
196 | // Create a path object to enclose the text.
197 | let frameRect = CGRect(x: currentOffset.x, y: drawingYoffset, width: textMaxWidth, height: textMaxHeight)
198 | let framePath = UIBezierPath(rect: frameRect).cgPath
199 |
200 | // Get the frame that will do the rendering.
201 | // The currentRange variable specifies only the starting point. The framesetter
202 | // lays out as much text as will fit into the frame.
203 | let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil)
204 |
205 | // Core Text draws from the bottom-left corner up, so flip
206 | // the current transform prior to drawing.
207 | currentContext.translateBy(x: 0, y: pageBounds.height + drawingYoffset - pageMarginBottom)
208 | currentContext.scaleBy(x: 1.0, y: -1.0)
209 |
210 | // Draw the frame.
211 | CTFrameDraw(frameRef, currentContext)
212 |
213 | // Pop state
214 | currentContext.restoreGState()
215 |
216 | // Update the current range based on what was drawn.
217 | let visibleRange = CTFrameGetVisibleStringRange(frameRef)
218 | currentRange = CFRange(location: visibleRange.location + visibleRange.length , length: 0)
219 |
220 | // Update last drawn frame
221 | let constraintSize = CGSize(width: textMaxWidth, height: textMaxHeight)
222 | let drawnSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, visibleRange, nil, constraintSize, nil)
223 | lastDrawnFrame = CGRect(x: currentOffset.x, y: drawingYoffset, width: drawnSize.width, height: drawnSize.height)
224 |
225 | // print(suggestionSize)
226 |
227 | // If we're at the end of the text, exit the loop.
228 | // print("\(currentRange.location) \(CFAttributedStringGetLength(currentText))")
229 | if currentRange.location == CFAttributedStringGetLength(currentText) {
230 | done = true
231 | // print("exit")
232 | } else {
233 | // begin a new page to draw text that is remaining.
234 | UIGraphicsBeginPDFPageWithInfo(pageBounds, nil)
235 | drawingYoffset = pageMarginTop
236 | // print("begin a new page to draw text that is remaining")
237 | }
238 |
239 |
240 | } while(!done)
241 |
242 | return lastDrawnFrame
243 | }
244 |
245 | /// - returns: drawing image rect
246 | fileprivate func drawImage(_ image: UIImage, alignment: ContentAlignment, currentOffset: CGPoint) -> CGRect {
247 |
248 | /* calculate the aspect size of image */
249 |
250 | let maxWidth = min( image.size.width, pageBounds.width )
251 | let maxHeight = min( image.size.height, pageBounds.height - currentOffset.y )
252 |
253 | let wFactor = image.size.width / maxWidth
254 | let hFactor = image.size.height / maxHeight
255 |
256 | let factor = max(wFactor, hFactor)
257 |
258 | let aspectWidth = image.size.width / factor
259 | let aspectHeight = image.size.height / factor
260 |
261 | /* calculate x offset for rendering */
262 | let renderingXoffset: CGFloat
263 | switch alignment {
264 | case .left:
265 | renderingXoffset = currentOffset.x
266 | case .center:
267 | renderingXoffset = ( pageBounds.width - currentOffset.x - aspectWidth ) / 2.0
268 | case .right:
269 | let right = pageBounds.width - pageMarginRight
270 | renderingXoffset = right - aspectWidth
271 | }
272 |
273 | let renderingRect = CGRect(x: renderingXoffset, y: currentOffset.y, width: aspectWidth, height: aspectHeight)
274 |
275 | // render image to current pdf context
276 | image.draw(in: renderingRect)
277 |
278 | return renderingRect
279 | }
280 |
281 | fileprivate func drawLineSeparator(height: CGFloat, currentOffset: CGPoint) -> CGRect {
282 |
283 | let drawRect = CGRect(x: currentOffset.x, y: currentOffset.y, width: pageBounds.width - pageMarginLeft - pageMarginRight, height: height)
284 | let path = UIBezierPath(rect: drawRect).cgPath
285 |
286 | // Get the graphics context.
287 | let currentContext = UIGraphicsGetCurrentContext()!
288 |
289 | // Set color
290 | UIColor.black.setStroke()
291 | UIColor.black.setFill()
292 |
293 | // Draw path
294 | currentContext.addPath(path)
295 | currentContext.drawPath(using: .fillStroke)
296 |
297 | // print(drawRect)
298 |
299 | return drawRect
300 | }
301 |
302 | fileprivate func drawTable(rowCount: Int, alignment: ContentAlignment, columnCount: Int, rowHeight: CGFloat, columnWidth: CGFloat?, tableLineWidth: CGFloat, font: UIFont?, tableDefinition:TableDefinition?, dataArray: Array>, currentOffset: CGPoint) -> CGRect {
303 |
304 | let height = (CGFloat(rowCount)*rowHeight)
305 |
306 | let drawRect = CGRect(x: currentOffset.x, y: currentOffset.y, width: pageBounds.width - pageMarginLeft - pageMarginRight, height: height)
307 |
308 | UIColor.black.setStroke()
309 | UIColor.black.setFill()
310 |
311 | let tableWidth = { () -> CGFloat in
312 | if let cws = tableDefinition?.columnWidths {
313 | return cws.reduce(0, { (result, current) -> CGFloat in
314 | return result + current
315 | })
316 | } else if let cw = columnWidth {
317 | return CGFloat(columnCount) * cw
318 | }
319 |
320 | return 0 // default which should never be use, because either columnWidth, or columnsWidths is set
321 | }()
322 |
323 | for i in 0...rowCount {
324 | let newOrigin = drawRect.origin.y + rowHeight*CGFloat(i)
325 |
326 |
327 |
328 | let from = CGPoint(x: drawRect.origin.x, y: newOrigin)
329 | let to = CGPoint(x: drawRect.origin.x + tableWidth, y: newOrigin)
330 |
331 | drawLineFromPoint(from, to: to, lineWidth: tableLineWidth)
332 | }
333 |
334 | for i in 0...columnCount {
335 | let currentOffset = { () -> CGFloat in
336 | if let cws = tableDefinition?.columnWidths {
337 | var offset:CGFloat = 0
338 | for x in 0.. CGFloat in
360 | if let cws = tableDefinition?.columnWidths {
361 | var offset:CGFloat = 0
362 | for x in 0.. UIFont in
377 | if let f = tableDefinition?.fonts {
378 | if (f.count > j){
379 | return f[j]
380 | }
381 | } else if let f = font {
382 | return f
383 | }
384 |
385 | return UIFont.systemFont(ofSize: UIFont.systemFontSize)
386 | }()
387 |
388 | let currentTextColor = { () -> UIColor in
389 | if let t = tableDefinition?.textColors {
390 | if t.count > j {
391 | return t[j]
392 | }
393 | }
394 |
395 | return UIColor.black
396 | }()
397 |
398 | let currentColumnWidth = { () -> CGFloat in
399 | if let cw = tableDefinition?.columnWidths {
400 | if cw.count > j {
401 | return cw[j]
402 | }
403 | } else if let cw = columnWidth {
404 | return cw
405 | }
406 |
407 | return 100 // default which should never be use, because either columnWidth, or columnsWidths is set
408 | }()
409 |
410 | let frame = CGRect(x: newOriginX, y: newOriginY, width: currentColumnWidth, height: rowHeight)
411 | drawTextInCell(frame, text: dataArray[i][j] as NSString, alignment: alignment, font: currentFont, textColor: currentTextColor)
412 | }
413 | }
414 |
415 | return drawRect
416 | }
417 |
418 | fileprivate func drawLineFromPoint(_ from: CGPoint, to: CGPoint, lineWidth: CGFloat) {
419 | let context = UIGraphicsGetCurrentContext()!
420 | context.setLineWidth(lineWidth)
421 | let colorspace = CGColorSpaceCreateDeviceRGB()
422 | let color = CGColor(colorSpace: colorspace, components: [0.2, 0.2, 0.2, 1.0])
423 |
424 | context.setStrokeColor(color!)
425 | context.move(to: CGPoint(x: from.x, y: from.y))
426 | context.addLine(to: CGPoint(x: to.x, y: to.y))
427 |
428 | context.strokePath()
429 | }
430 |
431 | fileprivate func drawTextInCell(_ rect: CGRect, text: NSString, alignment: ContentAlignment, font: UIFont, textColor:UIColor) {
432 | let paraStyle = NSMutableParagraphStyle()
433 |
434 | let skew = 0.0
435 |
436 | let attributes: [NSAttributedString.Key: Any] = [
437 | .foregroundColor: textColor,
438 | .paragraphStyle: paraStyle,
439 | .obliqueness: skew,
440 | .font: font
441 | ]
442 |
443 | let size = text.size(withAttributes: attributes)
444 |
445 | let x:CGFloat = { () -> CGFloat in
446 | switch alignment {
447 | case .left:
448 | return 0
449 | case .center:
450 | return (rect.size.width - size.width)/2
451 | case .right:
452 | return rect.size.width - size.width
453 | }
454 | }()
455 | let y = (rect.size.height - size.height)/2
456 |
457 | text.draw(at: CGPoint(x: rect.origin.x + x, y: rect.origin.y + y), withAttributes: attributes)
458 | }
459 |
460 | enum ArrangementDirection {
461 | case horizontal
462 | case vertical
463 | }
464 |
465 | open func generatePDFdata() -> Data {
466 |
467 | let pdfData = NSMutableData()
468 |
469 | UIGraphicsBeginPDFContextToData(pdfData, pageBounds, nil)
470 | UIGraphicsBeginPDFPageWithInfo(pageBounds, nil)
471 |
472 | var currentOffset = CGPoint(x: pageMarginLeft, y: pageMarginTop)
473 | var alignment = ContentAlignment.left
474 | var arrangementDirection = ArrangementDirection.vertical
475 | var lastYOffset = currentOffset.y
476 |
477 | for command in commands {
478 |
479 | switch command{
480 | case let .addText(text, font, textColor):
481 | let textFrame = drawText(text, font: font, textColor: textColor, alignment: alignment, currentOffset: currentOffset)
482 | lastYOffset = textFrame.origin.y + textFrame.height
483 | switch arrangementDirection {
484 | case .horizontal:
485 | currentOffset = CGPoint(x: textFrame.origin.x + textFrame.width, y: currentOffset.y)
486 | case .vertical:
487 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
488 | }
489 |
490 | case let .addAttributedText(attributedText):
491 | let textFrame = drawAttributedText(attributedText, currentOffset: currentOffset)
492 | lastYOffset = textFrame.origin.y + textFrame.height
493 | switch arrangementDirection {
494 | case .horizontal:
495 | currentOffset = CGPoint(x: textFrame.origin.x + textFrame.width, y: currentOffset.y)
496 | case .vertical:
497 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
498 | }
499 |
500 | case let .addImage(image):
501 | let imageFrame = drawImage(image, alignment: alignment, currentOffset: currentOffset)
502 | lastYOffset = imageFrame.origin.y + imageFrame.height
503 | switch arrangementDirection {
504 | case .horizontal:
505 | currentOffset = CGPoint(x: imageFrame.origin.x + imageFrame.width, y: currentOffset.y)
506 | case .vertical:
507 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
508 | }
509 |
510 | case let .addLineSeparator(height: height):
511 | let drawRect = drawLineSeparator(height: height, currentOffset: currentOffset)
512 | lastYOffset = drawRect.origin.y + drawRect.height
513 | switch arrangementDirection {
514 | case .horizontal:
515 | currentOffset = CGPoint(x: drawRect.origin.x + drawRect.width, y: currentOffset.y)
516 | case .vertical:
517 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
518 | }
519 |
520 | case let .addLineSpace(space):
521 | lastYOffset = currentOffset.y + space
522 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
523 |
524 | case let .addHorizontalSpace(space):
525 | lastYOffset = currentOffset.y
526 | currentOffset = CGPoint(x: currentOffset.x + space, y: currentOffset.y)
527 |
528 | case let .addTable(rowCount, columnCount, rowHeight, columnWidth, tableLineWidth, font, tableDefinition, dataArray):
529 | let tableFrame = drawTable(rowCount: rowCount, alignment: alignment, columnCount: columnCount, rowHeight: rowHeight, columnWidth: columnWidth, tableLineWidth: tableLineWidth, font: font, tableDefinition: tableDefinition, dataArray: dataArray, currentOffset: currentOffset)
530 | lastYOffset = tableFrame.origin.y + tableFrame.height
531 | switch arrangementDirection {
532 | case .horizontal:
533 | currentOffset = CGPoint(x: tableFrame.origin.x + tableFrame.width, y: currentOffset.y)
534 | case .vertical:
535 | currentOffset = CGPoint(x: currentOffset.x, y: lastYOffset)
536 | }
537 |
538 | case let .setContentAlignment(newAlignment):
539 | alignment = newAlignment
540 |
541 | case .beginNewPage:
542 | UIGraphicsBeginPDFPageWithInfo(pageBounds, nil)
543 | currentOffset = CGPoint(x: pageMarginLeft, y: pageMarginTop)
544 | lastYOffset = currentOffset.y
545 |
546 | case .beginHorizontalArrangement:
547 | arrangementDirection = .horizontal
548 |
549 | case .endHorizontalArrangement:
550 | arrangementDirection = .vertical
551 | currentOffset = CGPoint(x: pageMarginLeft, y: lastYOffset)
552 | }
553 | }
554 |
555 | UIGraphicsEndPDFContext()
556 |
557 | return pdfData as Data
558 | }
559 |
560 | }
561 |
--------------------------------------------------------------------------------
/SimplePDFTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SimplePDFTests/SimplePDFTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimplePDFTests.swift
3 | // SimplePDFTests
4 | //
5 | // Created by Nutchaphon Rewik on 17/01/2016.
6 | // Copyright © 2016 Nutchaphon Rewik. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import SimplePDF
11 |
12 | class SimplePDFTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 |
28 | let a4PaperSize = CGSize(width: 595, height: 842)
29 | let pdf = SimplePDF(pageSize: a4PaperSize)
30 |
31 | // load test image
32 | let bundle = Bundle(for: type(of: self))
33 | let path = bundle.path(forResource: "simple_pdf_logo", ofType: "png")!
34 | let image = UIImage(contentsOfFile: path)!
35 |
36 | pdf.setContentAlignment(.center)
37 | pdf.addImage(image)
38 |
39 | pdf.addLineSpace(30)
40 |
41 | pdf.setContentAlignment(.left)
42 | pdf.addText("Normal text follows by line separator")
43 | pdf.addLineSeparator()
44 |
45 | pdf.addLineSpace(20.0)
46 |
47 | pdf.setContentAlignment(.right)
48 | pdf.addText("Text after set content alignment to .Right")
49 | pdf.addLineSpace(20.0)
50 |
51 | pdf.addText("Cras quis eros orci.\nLorem ipsum dolor sit amet, consectetur adipiscing elit.\nDonec mollis vitae mi ut lobortis.\nUt ultrices mi vel neque venenatis, ut efficitur metus eleifend. Sed pellentesque lobortis est quis maximus. Maecenas ultricies risus et enim consectetur, id lobortis ante porta. Quisque at euismod enim. Vestibulum faucibus purus non justo fringilla, sit amet iaculis ex pellentesque. Vestibulum eget vulputate diam, sit amet ornare sem. Duis at eros non tortor malesuada accumsan.\nNunc vel libero ut sapien dictum iaculis a vel odio. Quisque purus turpis, tristique auctor ex non, consectetur scelerisque lorem. Mauris est justo, sollicitudin sit amet nisi a, mattis posuere orci. Sed elementum est at est tristique gravida. Aliquam iaculis, metus facilisis varius viverra, nunc libero ultricies arcu, in accumsan sem nibh vel purus.")
52 |
53 | pdf.addLineSpace(30)
54 |
55 | pdf.setContentAlignment(.center)
56 |
57 | pdf.addText("Center Text")
58 | pdf.addLineSpace(20.0)
59 | pdf.addText("Cras varius leo ac lectus malesuada, ut rhoncus nunc blandit.\n In venenatis diam et vehicula suscipit.\n Aliquam in ante at dolor sodales consectetur ut semper quam.\n Vivamus massa purus, congue sed leo sed, lobortis consectetur lacus. Nunc sed tortor nec augue mattis faucibus. Sed malesuada metus in sapien efficitur, ut aliquet nisl lobortis. Vestibulum faucibus purus non justo fringilla, sit amet iaculis ex pellentesque. Vestibulum eget vulputate diam, sit amet ornare sem. Aliquam erat volutpat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin scelerisque posuere mi, non consequat mauris auctor a. Fusce lacinia auctor lectus a elementum.")
60 |
61 |
62 | pdf.addLineSpace(30.0)
63 |
64 | pdf.setContentAlignment(.left)
65 | let textString = "This is an example of long text. If the text doesn't fit in the current page. Simple pdf will draw a part of text, and automatically begin a new page to draw the remaining text. This process will be repeated until there's no text left to draw. "
66 | pdf.addText(textString)
67 |
68 |
69 | pdf.beginNewPage()
70 | pdf.addText("Begin new page")
71 |
72 | if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
73 |
74 | let fileName = "example.pdf"
75 | let documentsFileName = documentDirectories + "/" + fileName
76 |
77 | let pdfData = pdf.generatePDFdata()
78 | do{
79 | try pdfData.write(to: URL(fileURLWithPath: documentsFileName), options: .atomic)
80 | print("\nThe generated pdf can be found at:")
81 | print("\n\t\(documentsFileName)\n")
82 | }catch{
83 | print(error)
84 | }
85 | }
86 |
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/SimplePDFTests/simple_pdf_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nRewik/SimplePDF/35e3e7ea6500eaf9244d66b9e6d316f7b85072aa/SimplePDFTests/simple_pdf_logo.png
--------------------------------------------------------------------------------