diff --git a/Example/Podfile.lock b/Example/Podfile.lock index e0df6f0a..f8062dde 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 910dc4431e42740070caa94787b39c629e120ed3 -COCOAPODS: 1.1.1 +COCOAPODS: 1.2.0.beta.1 diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index e0df6f0a..f8062dde 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 910dc4431e42740070caa94787b39c629e120ed3 -COCOAPODS: 1.1.1 +COCOAPODS: 1.2.0.beta.1 diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 20a62376..c1420c3b 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -7,19 +7,20 @@ objects = { /* Begin PBXBuildFile section */ - 1023DEBBA8045EF066B422620384A5DC /* PDFGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A82AE9367BDB098AFFDE82A429858F /* PDFGenerator.swift */; }; 2140F2D15846CFE413CB5C332F826747 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 616BEB51ECCAD129BDBCB7A956B56CC6 /* Foundation.framework */; }; - 2FA388C2B87EADFEBED0ECE626D8AFA1 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0FC611D529F27B4DDF7470DA13491C2 /* Command.swift */; }; - 52549B1928C026BD8F82D7751A15DF88 /* TPPDF-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5229E57EB7804657849C5D6EB7F8A7AC /* TPPDF-dummy.m */; }; + 31ACB386FB76B0AF7270BA0D1A3C7A33 /* PDFInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB82EDD27652EC45EC1B506A0F00E2B9 /* PDFInfo.swift */; }; + 38F79BEBF336F4654112F2356DE36C63 /* PDFGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FDE5C540484B3C5FB55279A73D7B1A /* PDFGenerator.swift */; }; 5B6A4D08B0CCD52CCDC819EEEAAE10B7 /* Pods-TPPDF_Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC652FF540FEA54EE65C9222344EB11 /* Pods-TPPDF_Example-dummy.m */; }; - 7568959D764E950264DA88286EE2CE6B /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2865CB5A3834B1ADC2AB60689C21D4FC /* Container.swift */; }; 8D01B502BA0D3721AD3066732EA99CBD /* Pods-TPPDF_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = EC5618E9464F0B85C78C826D6552B2F7 /* Pods-TPPDF_Tests-dummy.m */; }; AA4E407425645BEB30C5168BC47E93F2 /* TPPDF-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CA4E35BFF7E493BFBD9E39E1D2F3C97F /* TPPDF-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; B78CC2DBE4FA29DC5B4C38579A86222E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 616BEB51ECCAD129BDBCB7A956B56CC6 /* Foundation.framework */; }; BDEFDA3A304F2C1082D390ABC8D1A9D0 /* Pods-TPPDF_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5823F7923A03A8F51F473D60FF4B14DE /* Pods-TPPDF_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0820CB5053F708C617F90BB756CA1AB /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEDACB6AC77162E2BB0F2775E851DBA /* Container.swift */; }; + D7E35E06EDEE74F5C286E41FC9E68231 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC3A795B0216E961DAC69F10292168C /* Command.swift */; }; + DB9050CF3ABDDF0B638F01C5E0310A9E /* TPPDF-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5229E57EB7804657849C5D6EB7F8A7AC /* TPPDF-dummy.m */; }; EC677853D9129B98C0A8760998F39B93 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7EC994CDC2D681BA26389F78A7E4B325 /* UIKit.framework */; }; EDA61C6AE6FFEE15DAFC44CCFB20921F /* Pods-TPPDF_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 347C5AE9C83C04B369C778ED3A10F68B /* Pods-TPPDF_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F12BE1ACD46C1F489D7DFEFD06E4CBC2 /* PageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38E9B01C01FC449684A66EFCA8EDA32 /* PageFormat.swift */; }; + F40A4E0B94302371B90ABF327B71C086 /* PageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C730E89D5FBA5B9E5DECEBBE5C8D7C72 /* PageFormat.swift */; }; F6EA2CB67C8F281D384DBD72BFAAB5DD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 616BEB51ECCAD129BDBCB7A956B56CC6 /* Foundation.framework */; }; /* End PBXBuildFile section */ @@ -35,11 +36,11 @@ /* Begin PBXFileReference section */ 07E7ADD15E1894951F66BA3A6B4F9EB7 /* Pods-TPPDF_Tests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TPPDF_Tests-acknowledgements.markdown"; sourceTree = ""; }; + 0DEDACB6AC77162E2BB0F2775E851DBA /* Container.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; 0E7651E9568BAC8F9345CD0DCC788815 /* Pods-TPPDF_Tests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TPPDF_Tests-frameworks.sh"; sourceTree = ""; }; 1D202E93C0D73225DE357F30B42C1EAB /* Pods-TPPDF_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TPPDF_Example-acknowledgements.plist"; sourceTree = ""; }; 1E138EE8A4E866DBE739992BEB5B8C58 /* Pods-TPPDF_Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-TPPDF_Example-acknowledgements.markdown"; sourceTree = ""; }; 25CC4DED201A3D0DBC73DBBCEEDFFE03 /* Pods-TPPDF_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TPPDF_Tests.release.xcconfig"; sourceTree = ""; }; - 2865CB5A3834B1ADC2AB60689C21D4FC /* Container.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; 347C5AE9C83C04B369C778ED3A10F68B /* Pods-TPPDF_Example-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-TPPDF_Example-umbrella.h"; sourceTree = ""; }; 37E2B33283EF5AFC186C0F67ED4E118C /* TPPDF-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TPPDF-prefix.pch"; sourceTree = ""; }; 3E90765E7BDDF36F697BD7084A5A3707 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -53,23 +54,24 @@ 8024FE3270E884F9FD724089CBA9172F /* Pods_TPPDF_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TPPDF_Tests.framework; path = "Pods-TPPDF_Tests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; 8515A60A596F397A6B737DB3222DED70 /* Pods-TPPDF_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TPPDF_Example.debug.xcconfig"; sourceTree = ""; }; 8BB5A32ADC99AA8A828511C346B4A90B /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 92FDE5C540484B3C5FB55279A73D7B1A /* PDFGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFGenerator.swift; sourceTree = ""; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9CA6BAC2B3DC48E96A8C742B071EE55A /* Pods-TPPDF_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TPPDF_Tests.debug.xcconfig"; sourceTree = ""; }; - A0FC611D529F27B4DDF7470DA13491C2 /* Command.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; A15173E078C75C48E506A11D2072FBFB /* Pods-TPPDF_Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-TPPDF_Tests.modulemap"; sourceTree = ""; }; A876863E91E4A5D5FBBE3218533ADCDF /* TPPDF.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = TPPDF.xcconfig; sourceTree = ""; }; B8459F70B34893C050E1A4FFB015D41F /* Pods_TPPDF_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_TPPDF_Example.framework; path = "Pods-TPPDF_Example.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; B868EFD8FCB073799C299D886D3F93C6 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BCF9AB68E819DFAC82C187B6F8EEA801 /* TPPDF.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = TPPDF.framework; path = TPPDF.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C4A82AE9367BDB098AFFDE82A429858F /* PDFGenerator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFGenerator.swift; sourceTree = ""; }; + C730E89D5FBA5B9E5DECEBBE5C8D7C72 /* PageFormat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PageFormat.swift; sourceTree = ""; }; CA4E35BFF7E493BFBD9E39E1D2F3C97F /* TPPDF-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "TPPDF-umbrella.h"; sourceTree = ""; }; E954B7FFD4B8E56AD27E0D81809ADBF5 /* Pods-TPPDF_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-TPPDF_Example.release.xcconfig"; sourceTree = ""; }; EA2B3EA1FF310A9E3A4655CD147A1BF9 /* Pods-TPPDF_Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-TPPDF_Tests-acknowledgements.plist"; sourceTree = ""; }; + EB82EDD27652EC45EC1B506A0F00E2B9 /* PDFInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PDFInfo.swift; sourceTree = ""; }; EC5618E9464F0B85C78C826D6552B2F7 /* Pods-TPPDF_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TPPDF_Tests-dummy.m"; sourceTree = ""; }; F09895405AE93850E6CC620AA371ED37 /* TPPDF.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = TPPDF.modulemap; sourceTree = ""; }; - F38E9B01C01FC449684A66EFCA8EDA32 /* PageFormat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PageFormat.swift; sourceTree = ""; }; F394645AD30A616BBD19EBF1215DCF4B /* Pods-TPPDF_Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-TPPDF_Example-frameworks.sh"; sourceTree = ""; }; FBC652FF540FEA54EE65C9222344EB11 /* Pods-TPPDF_Example-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-TPPDF_Example-dummy.m"; sourceTree = ""; }; + FCC3A795B0216E961DAC69F10292168C /* Command.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -133,6 +135,15 @@ name = Products; sourceTree = ""; }; + 61CA518F5CAF384CAD8947088C9E47D0 /* TPPDF */ = { + isa = PBXGroup; + children = ( + BB917AD6EDA29C6B70D629EEA6032B14 /* Classes */, + ); + name = TPPDF; + path = TPPDF; + sourceTree = ""; + }; 7DB346D0F39D3F0E887471402A8071AB = { isa = PBXGroup; children = ( @@ -148,21 +159,12 @@ isa = PBXGroup; children = ( 1B17BDFC121767E716E35BE3AF5B7FC0 /* Support Files */, - 98D696DE6783EF9F613B9138E38CF857 /* TPPDF */, + 61CA518F5CAF384CAD8947088C9E47D0 /* TPPDF */, ); name = TPPDF; path = ../..; sourceTree = ""; }; - 98D696DE6783EF9F613B9138E38CF857 /* TPPDF */ = { - isa = PBXGroup; - children = ( - C450CDAA79E40B35E100392609194FFB /* Classes */, - ); - name = TPPDF; - path = TPPDF; - sourceTree = ""; - }; AB4BBF5F2FC47972689BE874061C358A /* Targets Support Files */ = { isa = PBXGroup; children = ( @@ -180,13 +182,14 @@ name = "Development Pods"; sourceTree = ""; }; - C450CDAA79E40B35E100392609194FFB /* Classes */ = { + BB917AD6EDA29C6B70D629EEA6032B14 /* Classes */ = { isa = PBXGroup; children = ( - A0FC611D529F27B4DDF7470DA13491C2 /* Command.swift */, - 2865CB5A3834B1ADC2AB60689C21D4FC /* Container.swift */, - F38E9B01C01FC449684A66EFCA8EDA32 /* PageFormat.swift */, - C4A82AE9367BDB098AFFDE82A429858F /* PDFGenerator.swift */, + FCC3A795B0216E961DAC69F10292168C /* Command.swift */, + 0DEDACB6AC77162E2BB0F2775E851DBA /* Container.swift */, + C730E89D5FBA5B9E5DECEBBE5C8D7C72 /* PageFormat.swift */, + 92FDE5C540484B3C5FB55279A73D7B1A /* PDFGenerator.swift */, + EB82EDD27652EC45EC1B506A0F00E2B9 /* PDFInfo.swift */, ); name = Classes; path = Classes; @@ -288,7 +291,7 @@ isa = PBXNativeTarget; buildConfigurationList = 90C0F02D8CB744EA840D0182C2445188 /* Build configuration list for PBXNativeTarget "TPPDF" */; buildPhases = ( - 6BAE85CEC526DB97ECEBEEB9E1647086 /* Sources */, + 22C4892C355F787D127F84FCC8DAA53B /* Sources */, 11A80242C18603E9B5DF7B5A961C808F /* Frameworks */, E5CEA08382E58329409BFC42A47F67AF /* Headers */, ); @@ -348,31 +351,32 @@ /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ - 241E83B45AE3E969EC84C03B33A09BC3 /* Sources */ = { + 22C4892C355F787D127F84FCC8DAA53B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5B6A4D08B0CCD52CCDC819EEEAAE10B7 /* Pods-TPPDF_Example-dummy.m in Sources */, + D7E35E06EDEE74F5C286E41FC9E68231 /* Command.swift in Sources */, + C0820CB5053F708C617F90BB756CA1AB /* Container.swift in Sources */, + F40A4E0B94302371B90ABF327B71C086 /* PageFormat.swift in Sources */, + 38F79BEBF336F4654112F2356DE36C63 /* PDFGenerator.swift in Sources */, + 31ACB386FB76B0AF7270BA0D1A3C7A33 /* PDFInfo.swift in Sources */, + DB9050CF3ABDDF0B638F01C5E0310A9E /* TPPDF-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 4034BE8F77975B0DDD9048AA7197E611 /* Sources */ = { + 241E83B45AE3E969EC84C03B33A09BC3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8D01B502BA0D3721AD3066732EA99CBD /* Pods-TPPDF_Tests-dummy.m in Sources */, + 5B6A4D08B0CCD52CCDC819EEEAAE10B7 /* Pods-TPPDF_Example-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 6BAE85CEC526DB97ECEBEEB9E1647086 /* Sources */ = { + 4034BE8F77975B0DDD9048AA7197E611 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2FA388C2B87EADFEBED0ECE626D8AFA1 /* Command.swift in Sources */, - 7568959D764E950264DA88286EE2CE6B /* Container.swift in Sources */, - F12BE1ACD46C1F489D7DFEFD06E4CBC2 /* PageFormat.swift in Sources */, - 1023DEBBA8045EF066B422620384A5DC /* PDFGenerator.swift in Sources */, - 52549B1928C026BD8F82D7751A15DF88 /* TPPDF-dummy.m in Sources */, + 8D01B502BA0D3721AD3066732EA99CBD /* Pods-TPPDF_Tests-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-frameworks.sh b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-frameworks.sh index f446f4e4..7e336b71 100755 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-frameworks.sh +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-frameworks.sh @@ -59,8 +59,13 @@ code_sign_if_enabled() { if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then # Use the current code_sign_identitiy echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" fi } @@ -89,3 +94,6 @@ fi if [[ "$CONFIGURATION" == "Release" ]]; then install_framework "$BUILT_PRODUCTS_DIR/TPPDF/TPPDF.framework" fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-resources.sh b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-resources.sh index 25e9d377..4602c68a 100755 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-resources.sh +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-resources.sh @@ -18,6 +18,9 @@ case "${TARGETED_DEVICE_FAMILY}" in 2) TARGET_DEVICE_ARGS="--target-device ipad" ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; *) TARGET_DEVICE_ARGS="--target-device mac" ;; diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-umbrella.h b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-umbrella.h index aad0e37c..eeedd4aa 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-umbrella.h +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example-umbrella.h @@ -1,5 +1,13 @@ #ifdef __OBJC__ #import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif #endif diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.debug.xcconfig b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.debug.xcconfig index 6b3300d1..492d1a5a 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.debug.xcconfig +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.debug.xcconfig @@ -1,5 +1,4 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TPPDF" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.release.xcconfig b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.release.xcconfig index 6b3300d1..492d1a5a 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.release.xcconfig +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.release.xcconfig @@ -1,5 +1,4 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -EMBEDDED_CONTENT_CONTAINS_SWIFT = YES FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TPPDF" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-frameworks.sh b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-frameworks.sh index 893c16a6..d839f602 100755 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-frameworks.sh +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-frameworks.sh @@ -59,8 +59,13 @@ code_sign_if_enabled() { if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then # Use the current code_sign_identitiy echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" - /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" fi } @@ -82,3 +87,6 @@ strip_invalid_archs() { fi } +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-resources.sh b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-resources.sh index 25e9d377..4602c68a 100755 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-resources.sh +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-resources.sh @@ -18,6 +18,9 @@ case "${TARGETED_DEVICE_FAMILY}" in 2) TARGET_DEVICE_ARGS="--target-device ipad" ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; *) TARGET_DEVICE_ARGS="--target-device mac" ;; diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-umbrella.h b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-umbrella.h index 1413cdc0..75d00130 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-umbrella.h +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests-umbrella.h @@ -1,5 +1,13 @@ #ifdef __OBJC__ #import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif #endif diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.debug.xcconfig b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.debug.xcconfig index 002e7159..312caf32 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.debug.xcconfig +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.debug.xcconfig @@ -1,4 +1,3 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TPPDF" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' diff --git a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.release.xcconfig b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.release.xcconfig index 002e7159..312caf32 100644 --- a/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.release.xcconfig +++ b/Example/Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.release.xcconfig @@ -1,4 +1,3 @@ -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/TPPDF" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' diff --git a/Example/Pods/Target Support Files/TPPDF/TPPDF-prefix.pch b/Example/Pods/Target Support Files/TPPDF/TPPDF-prefix.pch index aa992a4a..beb2a244 100644 --- a/Example/Pods/Target Support Files/TPPDF/TPPDF-prefix.pch +++ b/Example/Pods/Target Support Files/TPPDF/TPPDF-prefix.pch @@ -1,4 +1,12 @@ #ifdef __OBJC__ #import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif #endif diff --git a/Example/Pods/Target Support Files/TPPDF/TPPDF-umbrella.h b/Example/Pods/Target Support Files/TPPDF/TPPDF-umbrella.h index 00029590..37630f51 100644 --- a/Example/Pods/Target Support Files/TPPDF/TPPDF-umbrella.h +++ b/Example/Pods/Target Support Files/TPPDF/TPPDF-umbrella.h @@ -1,5 +1,13 @@ #ifdef __OBJC__ #import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif #endif diff --git a/Example/Pods/Target Support Files/TPPDF/TPPDF.xcconfig b/Example/Pods/Target Support Files/TPPDF/TPPDF.xcconfig index 19016b25..73c5f913 100644 --- a/Example/Pods/Target Support Files/TPPDF/TPPDF.xcconfig +++ b/Example/Pods/Target Support Files/TPPDF/TPPDF.xcconfig @@ -6,5 +6,6 @@ OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" PODS_BUILD_DIR = $BUILD_DIR PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} SKIP_INSTALL = YES diff --git a/Example/TPPDF.xcodeproj/project.pbxproj b/Example/TPPDF.xcodeproj/project.pbxproj index 7c9f6926..d4b6cdb2 100644 --- a/Example/TPPDF.xcodeproj/project.pbxproj +++ b/Example/TPPDF.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; + AAF20D711DEFCA6100948166 /* PortraitImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = AAF20D701DEFCA6100948166 /* PortraitImage.jpg */; }; D4032E8C1D629AF700F1C14A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D4032E8A1D629AF700F1C14A /* LaunchScreen.storyboard */; }; D4032E901D629B5D00F1C14A /* Image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = D4032E8F1D629B5D00F1C14A /* Image.jpg */; }; FE72AAD5840A908C7220B22D /* Pods_TPPDF_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3BAEA99CE7B3E2607E018AB /* Pods_TPPDF_Example.framework */; }; @@ -43,6 +44,7 @@ 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 81E2E4032A07FFB898A01D27 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + AAF20D701DEFCA6100948166 /* PortraitImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = PortraitImage.jpg; sourceTree = ""; }; BED306E4A2B7E0694D47D195 /* Pods-TPPDF_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TPPDF_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-TPPDF_Example/Pods-TPPDF_Example.release.xcconfig"; sourceTree = ""; }; D17FC7694F2FC04588A6AD27 /* Pods-TPPDF_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TPPDF_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-TPPDF_Tests/Pods-TPPDF_Tests.release.xcconfig"; sourceTree = ""; }; D4032E8B1D629AF700F1C14A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -101,6 +103,7 @@ 607FACD91AFB9204008FA782 /* Main.storyboard */, D4032E8A1D629AF700F1C14A /* LaunchScreen.storyboard */, D4032E8F1D629B5D00F1C14A /* Image.jpg */, + AAF20D701DEFCA6100948166 /* PortraitImage.jpg */, 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACD31AFB9204008FA782 /* Supporting Files */, ); @@ -258,6 +261,7 @@ D4032E8C1D629AF700F1C14A /* LaunchScreen.storyboard in Resources */, 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, + AAF20D711DEFCA6100948166 /* PortraitImage.jpg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -534,10 +538,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 13474A0CCA02688789744BB1 /* Pods-TPPDF_Tests.debug.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -554,10 +555,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D17FC7694F2FC04588A6AD27 /* Pods-TPPDF_Tests.release.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; diff --git a/Example/TPPDF/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/TPPDF/Images.xcassets/AppIcon.appiconset/Contents.json index d3942e94..b8236c65 100644 --- a/Example/TPPDF/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Example/TPPDF/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -35,4 +45,4 @@ "version" : 1, "author" : "xcode" } -} +} \ No newline at end of file diff --git a/Example/TPPDF/PortraitImage.jpg b/Example/TPPDF/PortraitImage.jpg new file mode 100644 index 00000000..b36145ae Binary files /dev/null and b/Example/TPPDF/PortraitImage.jpg differ diff --git a/Example/TPPDF/ViewController.swift b/Example/TPPDF/ViewController.swift index a3a3392b..67c927dd 100644 --- a/Example/TPPDF/ViewController.swift +++ b/Example/TPPDF/ViewController.swift @@ -17,10 +17,20 @@ class ViewController: UIViewController { } func generatePDF() { - let pdf = PDFGenerator(format: .a4) + let pdf = PDFGenerator(format: .a4, paginationContainer: .footerRight) + pdf.info.title = "Pasta with tomato sauce" + pdf.info.subject = "Recipe" + pdf.headerSpace = 50 + pdf.footerSpace = 25 + + let image = UIImage(named: "image.jpg")! + let portraitImage = UIImage(named: "PortraitImage.jpg")! pdf.addText(.footerCenter, text: "Created using TPPDF for iOS.") pdf.addText(.headerLeft, text: "Recipe: Pasta with tomato sauce") + pdf.addImage(.headerRight, image: image) + pdf.addLineSeparator(.headerCenter, thickness: 1.0) + pdf.addLineSeparator(.footerCenter, thickness: 0.1) let title = NSMutableAttributedString(string: "Pasta with tomato sauce", attributes: [ NSFontAttributeName : UIFont.systemFont(ofSize: 28.0), @@ -39,7 +49,7 @@ class ViewController: UIViewController { pdf.addLineSeparator(thickness: 0.1, color: UIColor.lightGray) pdf.addSpace(space: 12.0) - pdf.addImage(image: UIImage(named: "Image.jpg")!) + pdf.addImage(image: image) pdf.addSpace(space: 12.0) pdf.addLineSeparator(thickness: 0.1, color: UIColor.lightGray) @@ -48,9 +58,17 @@ class ViewController: UIViewController { let tableData: [[String]] = [ ["Rating", "4.5 / 5", "Prep\nTime:", "14 Hours" ], + ["Portions:", "14", "Cook\nTime:", "16 Minutes", ], + ["Portions:", "14", "Explain how to prep and cook this recipe here.\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", "16 Minutes", ], + ["Portions:", "14", "Cook\nTime:", "16 Minutes", ], + ["Portions:", "14", "Cook\nTime:", "16 Minutes", ], ["Portions:", "14", "Cook\nTime:", "16 Minutes", ] ] let tableAlignment: [[TableCellAlignment]] = [ + [.left, .center, .left, .center], + [.left, .center, .left, .center], + [.left, .center, .left, .center], + [.left, .center, .left, .center], [.left, .center, .left, .center], [.left, .center, .left, .center] ] @@ -58,9 +76,14 @@ class ViewController: UIViewController { 0.3, 0.2, 0.3, 0.2 ] pdf.addTable(data: tableData, alignment: tableAlignment, relativeColumnWidth: tableWidth, padding: 5, margin: 5, lineWidth: 0) + pdf.addText(text: "Explain how to prep and cook this recipe here.\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et ") pdf.addSpace(space: 12.0) + pdf.addImagesInRow(images: [image, portraitImage], captions: [NSAttributedString(string: "Pasta with tomato sauce"), NSAttributedString(string: "Pasta with tomato sauce")]) + pdf.addImage(.contentCenter, image: portraitImage, size: CGSize(width: portraitImage.size.width, height: portraitImage.size.height / 4), caption: NSAttributedString(string: "Pasta with tomato sauce")) + pdf.addImage(.contentCenter, image: image, sizeFit: .width) + let ingridients = NSMutableAttributedString(string: "Ingridients") ingridients.addAttributes([ NSFontAttributeName : UIFont.systemFont(ofSize: 20.0), @@ -73,7 +96,7 @@ class ViewController: UIViewController { let ingridientsString: String = { var result = "" - for i in 1...10 { + for i in 1...5 { result = result + "Ingridient \(i)\n" } return result @@ -99,16 +122,11 @@ class ViewController: UIViewController { pdf.setAbsoluteOffset(offset: 450) pdf.addText(text: "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet,") + pdf.createNewPage() + pdf.addText(text: "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet,") - let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] - let url = URL(fileURLWithPath: path).appendingPathComponent("temp.pdf") - let data = pdf.generatePDFdata() - do { - try data.write(to: url, options: .atomicWrite) - } catch { - print(error) - } - - (self.view as? UIWebView)?.load(data, mimeType: "application/pdf", textEncodingName: "utf-8", baseURL: URL(fileURLWithPath: "")) + + let url = pdf.generatePDFfile("Pasta with tomato sauce") + (self.view as? UIWebView)?.loadRequest(URLRequest(url: url)) } } diff --git a/README.md b/README.md index 50f08289..013365c2 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ TPPDF is a PDF builder for iOS, based on the [Builder](https://en.wikipedia.org/ - [x] Horizontal line separators - [x] Custom indentation - [x] Custom top offset (good for layered rendering) +- [x] Pagination +- [x] Image caption +- [x] Compress images +- [x] Custom image size fit +- [x] Image in the header and footer +- [x] Horizontal line separators in the header and footer +- [x] Generate PDF files directly to handle large PDF files ([Details](http://stackoverflow.com/questions/14691264/how-can-i-lower-memory-climb-when-generating-large-pdfs)) +- [x] PDF metadata - You need more features? Checkout #Contribute ## Requirements @@ -124,6 +132,24 @@ Currently the following commands exist: - AddTable - SetIndentation - SetAbsoluteOffset +- AddImagesInRow +- createNewPage + +### PDFInfo + +Configure PDF metadata, including `title`, `author`, `subject`, `keywords`, `owner password`, `user password`, `allows printing`, and `allows copying`. + +The default PDF metadata is +```swift +title = "kf99916/TPPDF" +author = "kf99916/TPPDF" +subject = "https://github.com/kf99916/TPPDF" +keywords = "tppdf,pdf,kf99916" +ownerPassword = nil +userPassword = nil +allowsPrinting = true +allowsCopying = true +``` ### Container @@ -173,9 +199,9 @@ let title = NSMutableAttributedString(string: "Awesome attributed title!", attri pdf.addAttributedText(text: title) ``` -### AddImage(container, image, size) +### AddImage(container, image, size, caption, sizeFit) -Draws an image in the given container. If the given size is not zero size, it will draw it using that size, proportionally scaling. The size of an image is limited by the page bounds. +Draws an image in the given container. If the given size is not zero size, it will draw it using that size, proportionally scaling. The size of an image is scaled according to sizeFit. If the height of an image and its caption is beyond the page bounds, then a new page is created. **Example:** @@ -203,7 +229,7 @@ Draws a horizontal line using the given line thickness and color in the given co pdf.addLineSeparator(thickness: 0.1, color: UIColor.lightGrayColor()) ``` -### AddTable(container, data, alignment, relativeColumnWidth, padding, margin, textColor, lineColor, lineWidth, drawCellBounds) +### AddTable(container, data, alignment, relativeColumnWidth, padding, margin, textColor, lineColor, lineWidth, drawCellBounds, textFont) Draws a table in the given container. @@ -251,6 +277,15 @@ The cell bounds are not the row/column grid of the table, but the lines between pdf.addTable(data: tableData, alignment: tableAlignment, relativeColumnWidth: tableWidth, padding: 5, margin: 5, textColor: UIColor.blackColor(), lineColor: UIColor.darkGrayColor(), lineWidth: 1.5, drawCellBounds: false) ``` +### AddImagesInRow(container, images, captions, spacing) + +Draws images with captions in the row using the given spacing in the given container. + +**Example:** + +```swift +pdf.addImagesInRow(images: [UIImage(named: "image.jpg")!, UIImage(named: "PortraitImage.jpg")!], captions: [NSAttributedString(string: "Caption 1"), NSAttributedString(string: "Caption 2")]) +``` ### SetIndentation(container, points) @@ -278,6 +313,17 @@ pdf.setOffset(offset: 250.0) One possible use case are layered PDF files. Simply call `pdf.setOffset(offset: 0.0)` and you can add content which is placed on top of the previously set content. +### createNewPage() + +Create a new page. + + +**Example:** + +```swift +pdf.createNewPage() +``` + ### Header & Footer If you want to add a text to the header or footer you simply need to choose the correct container. @@ -285,7 +331,7 @@ If you want to add a text to the header or footer you simply need to choose the But there are some limitations: - Only one line. If you want multiple lines, add multiple commands -- Currently only `AddText` and `AddAttributedText` are supported as header or footer command +- Currently only `AddText`, `AddAttributedText`, and `AddImage` are supported as header or footer command ## Communication @@ -342,6 +388,10 @@ Then, when calling the command, it changes the correct font variable, depending If you are using TPPDF in your app and want to be listed here, simply create a pull request or let me know on twitter or via github. I am always curious who is using my projects :) +[Hikingbook](https://itunes.apple.com/app/id1067838748) - by Zheng-Xiang Ke + +![Hikingbook](apps/Hikingbook.png) + [Mama's Cookbook (future release)](https://itunes.apple.com/us/app/mamas-cookbook/id1019090528) - by Philip Niedertscheider ![Mama's Cookbook](apps/MCB.png) diff --git a/TPPDF/Classes/Command.swift b/TPPDF/Classes/Command.swift index 930e03a7..84dd5b76 100644 --- a/TPPDF/Classes/Command.swift +++ b/TPPDF/Classes/Command.swift @@ -12,11 +12,14 @@ enum Command { case addText(text: String, lineSpacing: CGFloat) case addAttributedText(text: NSAttributedString) - case addImage(image: UIImage, size: CGSize) + case addImage(image: UIImage, size: CGSize, caption: NSAttributedString, sizeFit: ImageSizeFit) + case addImagesInRow(images: [UIImage], captions: [NSAttributedString], spacing: CGFloat) case addSpace(space: CGFloat) case addLineSeparator(thickness: CGFloat, color: UIColor) - case addTable(data: [[String]], alignment: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat, margin: CGFloat, textColor: UIColor, lineColor: UIColor, lineWidth: CGFloat, drawCellBounds: Bool) + case addTable(data: [[String]], alignment: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat, margin: CGFloat, textColor: UIColor, lineColor: UIColor, lineWidth: CGFloat, drawCellBounds: Bool, textFont: UIFont) case setIndentation(points: CGFloat) case setOffset(points: CGFloat) + + case createNewPage() } diff --git a/TPPDF/Classes/Container.swift b/TPPDF/Classes/Container.swift index b5a435f1..280bc923 100644 --- a/TPPDF/Classes/Container.swift +++ b/TPPDF/Classes/Container.swift @@ -90,3 +90,7 @@ public enum TableCellAlignment { } } } + +public enum ImageSizeFit { + case width, height, widthHeight +} diff --git a/TPPDF/Classes/PDFGenerator.swift b/TPPDF/Classes/PDFGenerator.swift index 63c2102f..5711eb0f 100644 --- a/TPPDF/Classes/PDFGenerator.swift +++ b/TPPDF/Classes/PDFGenerator.swift @@ -12,6 +12,8 @@ open class PDFGenerator { // MARK: - Public Variables + static let headerImageHeight: CGFloat = 32 + open var pageBounds: CGRect = CGRect.zero open var pageMargin: CGFloat = 0 @@ -21,6 +23,8 @@ open class PDFGenerator { open var headerSpace: CGFloat = 0 open var footerSpace: CGFloat = 0 + open var info: PDFInfo = PDFInfo() + // MARK: - Private Variables fileprivate var commands: [(Container, Command)] = [] @@ -33,6 +37,17 @@ open class PDFGenerator { return CGSize(width: pageBounds.width - 2 * pageMargin, height: pageBounds.height - maxHeaderHeight() - headerSpace - maxFooterHeight() - footerSpace) } + fileprivate var paginationContainer = Container.none + fileprivate var page = 1 + + fileprivate var imageQuality: CGFloat = 0.8 { + didSet { + if imageQuality > 1 { + imageQuality = 1 + } + } + } + fileprivate var headerFooterCommands: [(Container, Command)] = [] fileprivate let font = UIFont.systemFont(ofSize: UIFont.systemFontSize) @@ -44,7 +59,7 @@ open class PDFGenerator { // MARK: - Initializing - public init(pageSize: CGSize, pageMargin: CGFloat = 36.0, headerMargin: CGFloat = 20.0, footerMargin: CGFloat = 20.0, headerSpace: CGFloat = 8, footerSpace: CGFloat = 8) { + public init(pageSize: CGSize, pageMargin: CGFloat = 36.0, headerMargin: CGFloat = 20.0, footerMargin: CGFloat = 20.0, headerSpace: CGFloat = 8, footerSpace: CGFloat = 8, paginationContainer: Container = .none, imageQuality: CGFloat = 0.8, info: PDFInfo = PDFInfo()) { pageBounds = CGRect(origin: CGPoint.zero, size: pageSize) self.pageMargin = pageMargin @@ -54,10 +69,15 @@ open class PDFGenerator { self.headerSpace = headerSpace self.footerSpace = footerSpace + self.paginationContainer = paginationContainer + self.imageQuality = imageQuality + + self.info = info + resetHeaderFooterHeight() } - public init(format: PageFormat) { + public init(format: PageFormat, paginationContainer: Container = .none, imageQuality: CGFloat = 0.8, info: PDFInfo = PDFInfo()) { pageBounds = CGRect(origin: CGPoint.zero, size: format.size) pageMargin = format.margin @@ -67,6 +87,11 @@ open class PDFGenerator { headerSpace = format.headerSpace footerSpace = format.footerSpace + self.paginationContainer = paginationContainer + self.imageQuality = imageQuality + + self.info = info + resetHeaderFooterHeight() } @@ -80,8 +105,12 @@ open class PDFGenerator { commands += [(container, .addAttributedText(text: text)) ] } - open func addImage(_ container: Container = Container.contentLeft, image: UIImage, size: CGSize = CGSize.zero) { - commands += [(container, .addImage(image: image, size: size))] + open func addImage(_ container: Container = Container.contentLeft, image: UIImage, size: CGSize = CGSize.zero, caption: NSAttributedString = NSAttributedString(), sizeFit: ImageSizeFit = .widthHeight) { + commands += [(container, .addImage(image: image, size: size, caption: caption, sizeFit: sizeFit))] + } + + open func addImagesInRow(_ container: Container = Container.contentLeft, images: [UIImage], captions: [NSAttributedString] = [], spacing: CGFloat = 5.0) { + commands += [(container, .addImagesInRow(images: images, captions: captions, spacing: spacing))] } open func addSpace(_ container: Container = Container.contentLeft, space: CGFloat) { @@ -92,7 +121,7 @@ open class PDFGenerator { commands += [(container, .addLineSeparator(thickness: thickness, color: color))] } - open func addTable(_ container: Container = Container.contentLeft, data: [[String]], alignment: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat = 0, margin: CGFloat = 0, textColor: UIColor = UIColor.black, lineColor: UIColor = UIColor.darkGray, lineWidth: CGFloat = 1.0, drawCellBounds: Bool = false) { + open func addTable(_ container: Container = Container.contentLeft, data: [[String]], alignment: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat = 0, margin: CGFloat = 0, textColor: UIColor = UIColor.black, lineColor: UIColor = UIColor.darkGray, lineWidth: CGFloat = 1.0, drawCellBounds: Bool = false, textFont: UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)) { assert(data.count != 0, "You can't draw an table without rows!") assert(data.count == alignment.count, "Data and alignment array must be equal size!") for (rowIdx, row) in data.enumerated() { @@ -100,7 +129,7 @@ open class PDFGenerator { assert(row.count == relativeColumnWidth.count, "Data and alignment for row with index \(rowIdx) does not have the same amount!") } - commands += [(container, .addTable(data: data, alignment: alignment, relativeColumnWidth: relativeColumnWidth, padding: padding, margin: margin, textColor: textColor, lineColor: lineColor, lineWidth: lineWidth, drawCellBounds: drawCellBounds))] + commands += [(container, .addTable(data: data, alignment: alignment, relativeColumnWidth: relativeColumnWidth, padding: padding, margin: margin, textColor: textColor, lineColor: lineColor, lineWidth: lineWidth, drawCellBounds: drawCellBounds, textFont: textFont))] } open func setIndentation(_ container: Container = Container.contentLeft, indent: CGFloat) { @@ -111,12 +140,34 @@ open class PDFGenerator { commands += [(container, .setOffset(points: offset))] } + open func createNewPage() { + commands += [(.contentLeft, .createNewPage())] + } + // MARK: - Generation open func generatePDFdata(_ progress: ((CGFloat) -> ())? = nil) -> Data { let pdfData = NSMutableData() - UIGraphicsBeginPDFContextToData(pdfData, pageBounds, nil) + UIGraphicsBeginPDFContextToData(pdfData, pageBounds, generateDocumentInfo()) + generatePDFContext(progress: progress) + UIGraphicsEndPDFContext() + + return pdfData as Data + } + + open func generatePDFfile(_ fileName: String, progress: ((CGFloat) -> ())? = nil) -> URL { + let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName).appendingPathExtension("pdf") + + + UIGraphicsBeginPDFContextToFile(url.path, pageBounds, generateDocumentInfo()) + generatePDFContext(progress: progress) + UIGraphicsEndPDFContext() + + return url; + } + + fileprivate func generatePDFContext(progress: ((CGFloat) -> ())?) { UIGraphicsBeginPDFPageWithInfo(pageBounds, nil) headerFooterCommands = commands.filter { return $0.0.isFooter || $0.0.isHeader } @@ -141,47 +192,28 @@ open class PDFGenerator { let count: CGFloat = CGFloat(contentCommands.count) for (idx, (container, command)) in contentCommands.enumerated() { - renderCommand(container, command: command) - progress?(CGFloat(idx + 1) / count) + autoreleasepool { + renderCommand(container, command: command) + progress?(CGFloat(idx + 1) / count) + } } - - UIGraphicsEndPDFContext() - - return pdfData as Data } // MARK: - Rendering - fileprivate func drawText(_ container: Container, text: String, font: UIFont, spacing: CGFloat, repeated: Bool = false) { - let paragraphStyle = NSMutableParagraphStyle() - switch container { - case .headerLeft, .contentLeft, .footerLeft: - paragraphStyle.alignment = .left - case .headerCenter, .contentCenter, .footerCenter: - paragraphStyle.alignment = .center - case .headerRight, .contentRight, .footerRight: - paragraphStyle.alignment = .right - default: - paragraphStyle.alignment = .left - } - - paragraphStyle.lineSpacing = spacing - - let attributes: [String:NSObject] = [ - NSFontAttributeName: font, - NSParagraphStyleAttributeName: paragraphStyle - ] + fileprivate func drawText(_ container: Container, text: String, font: UIFont, spacing: CGFloat, repeated: Bool = false, textMaxWidth: CGFloat = 0) { + let attributes = generateDefaultTextAttributes(container, font: font, spacing: spacing) - drawAttributedText(container, text: NSAttributedString(string: text, attributes: attributes)) + drawAttributedText(container, text: NSAttributedString(string: text, attributes: attributes), repeated: repeated, textMaxWidth: textMaxWidth) } - fileprivate func drawAttributedText(_ container: Container, text: NSAttributedString, repeated: Bool = false) { + fileprivate func drawAttributedText(_ container: Container, text: NSAttributedString, repeated: Bool = false, textMaxWidth: CGFloat = 0) { let currentText = CFAttributedStringCreateCopy(nil, text as CFAttributedString) - let framesetter = CTFramesetterCreateWithAttributedString(currentText!) var currentRange = CFRange(location: 0, length: 0) var done = false repeat { + let (frameRef, drawnSize) = calculateTextFrameAndDrawnSizeInOnePage(container, text: currentText!, currentRange: currentRange, textMaxWidth: textMaxWidth) // Get the graphics context. let currentContext = UIGraphicsGetCurrentContext()! @@ -192,34 +224,6 @@ open class PDFGenerator { // that no old scaling factors are left in place. currentContext.textMatrix = CGAffineTransform.identity - let textMaxWidth = pageBounds.width - 2 * pageMargin - indentation[container.normalize]! - let textMaxHeight: CGFloat = { - if container.isHeader { - return pageBounds.height - headerHeight[container]! - } else if container.isFooter { - return footerMargin - } else { - return pageBounds.height - maxHeaderHeight() - headerSpace - maxFooterHeight() - footerSpace - contentHeight - } - }() - - // Create a path object to enclose the text. - let frame: CGRect = { - if container.isHeader { - return CGRect(x: pageMargin + indentation[container.normalize]!, y: 0, width: textMaxWidth, height: textMaxHeight) - } else if container.isFooter { - return CGRect(x: pageMargin + indentation[container.normalize]!, y: footerHeight[container]!, width: textMaxWidth, height: textMaxHeight) - } else { - return CGRect(x: pageMargin + indentation[container.normalize]!, y: maxFooterHeight() + footerSpace, width: textMaxWidth, height: textMaxHeight) - } - }() - let framePath = UIBezierPath(rect: frame).cgPath - - // Get the frame that will do the rendering. - // The currentRange variable specifies only the starting point. The framesetter - // lays out as much text as will fit into the frame. - let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil) - // Core Text draws from the bottom-left corner up, so flip // the current transform prior to drawing. currentContext.translateBy(x: 0, y: pageBounds.height) @@ -235,10 +239,6 @@ open class PDFGenerator { let visibleRange = CTFrameGetVisibleStringRange(frameRef) currentRange = CFRange(location: visibleRange.location + visibleRange.length , length: 0) - // Update last drawn frame - let constraintSize = CGSize(width: textMaxWidth, height: textMaxHeight) - let drawnSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, visibleRange, nil, constraintSize, nil) - if container.isHeader { headerHeight[container] = headerHeight[container]! + drawnSize.height } else if container.isFooter { @@ -246,59 +246,157 @@ open class PDFGenerator { } else { contentHeight += drawnSize.height } - if currentRange.location == CFAttributedStringGetLength(currentText){ + if currentRange.location == CFAttributedStringGetLength(currentText) { done = true } else { - UIGraphicsBeginPDFPageWithInfo(pageBounds, nil) - contentHeight = 0 - - renderHeaderFooter() + generateNewPage() } } while(!done) } - fileprivate func drawImage(_ container: Container, image: UIImage, size: CGSize) { - var maxWidth: CGFloat = 0 - var maxHeight: CGFloat = 0 + fileprivate func drawImage(_ container: Container, image: UIImage, frame: CGRect, caption: NSAttributedString) { + // resize + let resizeFactor = (3 * imageQuality > 1) ? 3 * imageQuality : 1 + let resizeImageSize = CGSize(width: frame.size.width * resizeFactor, height: frame.size.height * resizeFactor) + UIGraphicsBeginImageContext(resizeImageSize) + image.draw(in: CGRect(x:0, y:0, width: resizeImageSize.width, height: resizeImageSize.height)) + var compressedImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + // compression + if let image = compressedImage, let jpegData = UIImageJPEGRepresentation(image, imageQuality) { + compressedImage = UIImage(data: jpegData) + } + + if let resultImage = compressedImage { + resultImage.draw(in: frame) + } + else { + image.draw(in: frame) + } - /* calculate the aspect size of image */ - if size == CGSize.zero { - maxWidth = min(image.size.width, contentSize.width - indentation[container.normalize]!) - maxHeight = min(image.size.height, contentSize.height) + if container.isHeader { + headerHeight[container] = headerHeight[container]! + frame.height + } else if container.isFooter { + footerHeight[container] = footerHeight[container]! + frame.height } else { - maxWidth = min(size.width, contentSize.width - indentation[container.normalize]!) - maxHeight = min(size.width, contentSize.height - contentHeight) + contentHeight += frame.height } - let wFactor = image.size.width / maxWidth - let hFactor = image.size.height / maxHeight - - let factor = max(wFactor, hFactor) - let aspectWidth = image.size.width / factor - let aspectHeight = image.size.height / factor + if caption.length > 0 { + drawAttributedText(container, text: caption, textMaxWidth: frame.size.width) + } + } + + fileprivate func drawImage(_ container: Container, image: UIImage, size: CGSize, caption: NSAttributedString, sizeFit: ImageSizeFit) { + var (imageSize, captionSize) = calculateImageCaptionSize(container, image: image, size: size, caption: caption, sizeFit: sizeFit) + + var y: CGFloat = { + switch container.normalize { + case .headerLeft: + return headerHeight[container]! + case .contentLeft: + var y = contentHeight + maxHeaderHeight() + headerSpace + if (contentHeight + imageSize.height + captionSize.height > contentSize.height || + (sizeFit == .height && imageSize.height < size.height)) { + generateNewPage() + + (imageSize, captionSize) = calculateImageCaptionSize(container, image: image, size: size, caption: caption, sizeFit: sizeFit) + y = contentHeight + maxHeaderHeight() + headerSpace + } + return y + case .footerLeft: + return contentSize.height + maxHeaderHeight() + footerHeight[container]! + default: + return 0 + } + }() let x: CGFloat = { switch container { - case .contentLeft: + case .headerLeft, .contentLeft, .footerLeft: return pageMargin + indentation[container.normalize]! - case .contentCenter: - return pageBounds.midX - aspectWidth / 2 - case .contentRight: - return pageBounds.width - pageMargin - aspectWidth + case .headerCenter, .contentCenter, .footerCenter: + return pageBounds.midX - imageSize.width / 2 + case .headerRight, .contentRight, .footerRight: + return pageBounds.width - pageMargin - imageSize.width default: return 0 } }() - let frame = CGRect(x: x, y: contentHeight + maxHeaderHeight() + headerSpace, width: aspectWidth, height: aspectHeight) + let frame = CGRect(x: x, y: y, width: imageSize.width, height: imageSize.height) + drawImage(container, image: image, frame: frame, caption: caption) + } + + fileprivate func drawImagesInRow(_ container: Container, images: [UIImage], captions: [NSAttributedString], spacing: CGFloat) { + if (images.count <= 0) { + return + } + + let totalimagesWidth = contentSize.width - indentation[container.normalize]! - (CGFloat(images.count) - 1) * spacing + + let imageWidth = totalimagesWidth / CGFloat(images.count) + + let calculateImageCaptionSizes: ([UIImage], [NSAttributedString]) -> ([CGSize], CGFloat) = { + images, captions in + + var (imageSizes, maxHeight): ([CGSize], CGFloat) = ([], 0) + for (index, image) in images.enumerated() { + let caption = (captions.count > index) ? captions[index] : NSAttributedString() + let (imageSize, captionSize) = self.calculateImageCaptionSize(container, image: image, size: CGSize(width: imageWidth, height: image.size.height), caption: caption, sizeFit: .width) + imageSizes.append(imageSize) + + if maxHeight < imageSize.height + captionSize.height { + maxHeight = imageSize.height + captionSize.height + } + } + + return (imageSizes, maxHeight) + } - image.draw(in: frame) + var (imageSizes, maxHeight) = calculateImageCaptionSizes(images, captions) + + var y = contentHeight + maxHeaderHeight() + headerSpace + if (contentHeight + maxHeight > contentSize.height) { + generateNewPage() + + y = contentHeight + maxHeaderHeight() + headerSpace + (imageSizes, maxHeight) = calculateImageCaptionSizes(images, captions) + } + + var x: CGFloat = pageMargin + indentation[container.normalize]! + + let (nowContentHeight, nowIndentation) = (contentHeight, indentation[container.normalize]!) + for (index, image) in images.enumerated() { + let imageSize = imageSizes[index] + let caption = (captions.count > index) ? captions[index] : NSAttributedString() + drawImage(container, image: image, frame: CGRect(x: x, y: y, width: imageSize.width, height: imageSize.height), caption: caption) + + x += imageSize.width + spacing + indentation[container.normalize] = indentation[container.normalize]! + imageSize.width + spacing + contentHeight = nowContentHeight + } - contentHeight += frame.height + indentation[container.normalize] = nowIndentation + contentHeight += maxHeight } fileprivate func drawLineSeparator(_ container: Container, thickness: CGFloat, color: UIColor) { - let drawRect = CGRect(x: pageMargin + indentation[container.normalize]!, y: contentHeight + maxHeaderHeight() + headerSpace, width: contentSize.width - indentation[container.normalize]!, height: thickness) + let y: CGFloat = { + switch container.normalize { + case .headerLeft: + return maxHeaderHeight() + 4 + case .contentLeft: + return contentHeight + maxHeaderHeight() + headerSpace + case .footerLeft: + return contentSize.height + maxHeaderHeight() + headerSpace + footerSpace - 4 + default: + return 0 + } + }() + + let drawRect = CGRect(x: pageMargin + indentation[container.normalize]!, y: y, width: contentSize.width - indentation[container.normalize]!, height: thickness) let path = UIBezierPath(rect: drawRect).cgPath // Get the graphics context. @@ -313,7 +411,7 @@ open class PDFGenerator { currentContext.drawPath(using: .fillStroke) } - fileprivate func drawTable(_ container: Container, data: [[String]], alignments: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat, margin: CGFloat, textColor: UIColor, lineColor: UIColor, lineWidth: CGFloat, drawCellBounds: Bool) { + fileprivate func drawTable(_ container: Container, data: [[String]], alignments: [[TableCellAlignment]], relativeColumnWidth: [CGFloat], padding: CGFloat, margin: CGFloat, textColor: UIColor, lineColor: UIColor, lineWidth: CGFloat, drawCellBounds: Bool, textFont: UIFont) { assert(data.count != 0, "You can't draw an table without rows!") assert(data.count == alignments.count, "Data and alignment array must be equal size!") for (rowIdx, row) in data.enumerated() { @@ -331,7 +429,7 @@ open class PDFGenerator { let attributes: [String: AnyObject] = [ NSForegroundColorAttributeName: textColor, - NSFontAttributeName: font + NSFontAttributeName: textFont ] for (rowIdx, row) in data.enumerated() { @@ -344,8 +442,9 @@ open class PDFGenerator { // Calcuate X position and size for (colIdx, column) in row.enumerated() { + let text = NSAttributedString(string: column, attributes: attributes) let width = relativeColumnWidth[colIdx] * totalWidth - let result = calculateCellFrame(CGPoint(x: x, y: y + maxHeaderHeight() + headerSpace), width: width - 2 * margin - 2 * padding, text: column as NSString, alignment: alignments[rowIdx][colIdx], attributes: attributes) + let result = calculateCellFrame(CGPoint(x: x, y: y + maxHeaderHeight() + headerSpace), width: width - 2 * margin - 2 * padding, text: text, alignment: alignments[rowIdx][colIdx]) x += width maxHeight = max(maxHeight, result.height) frames[rowIdx].append(result) @@ -372,12 +471,34 @@ open class PDFGenerator { y += maxHeight + margin + padding } + // Divide tables according to contentSize + var (dataInThisPage, alignmentsInThisPage, framesInThisPage): ([[String]], [[TableCellAlignment]], [[CGRect]]) = ([], [], []) + var (dataInNewPage, alignmentsInNewPage): ([[String]], [[TableCellAlignment]]) = ([], []) + var totalHeight: CGFloat = 0 + for (rowIdx, row) in data.enumerated() { + let maxHeight = frames[rowIdx].reduce(0) { max($0, $1.height) } + let cellHeight = maxHeight + 2 * margin + 2 * padding + if (frames[rowIdx][0].origin.y + cellHeight > contentSize.height + maxHeaderHeight() + headerSpace) { + dataInNewPage.append(row) + alignmentsInNewPage.append(alignments[rowIdx]) + } + else { + dataInThisPage.append(row) + alignmentsInThisPage.append(alignments[rowIdx]) + framesInThisPage.append(frames[rowIdx]) + + totalHeight += cellHeight + } + } + // Draw text - for (rowIdx, row) in data.enumerated() { + for (rowIdx, row) in dataInThisPage.enumerated() { for (colIdx, text) in row.enumerated() { - let frame = frames[rowIdx][colIdx] - text.draw(at: frame.origin, withAttributes: attributes) + let frame = framesInThisPage[rowIdx][colIdx] + let attributedText = NSAttributedString(string: text, attributes: attributes) + // the last line of text is hidden if 30 is not added + attributedText.draw(in: CGRect(origin: frame.origin, size: CGSize(width: frame.width, height:frame.height + 20))) } } @@ -391,9 +512,9 @@ open class PDFGenerator { // Draw cell bounding box if drawCellBounds { - for (rowIdx, row) in data.enumerated() { + for (rowIdx, row) in dataInThisPage.enumerated() { for (colIdx, _) in row.enumerated() { - let frame = frames[rowIdx][colIdx] + let frame = framesInThisPage[rowIdx][colIdx] let borderFrame = CGRect(x: frame.minX - padding, y: frame.minY - padding, width: frame.width + 2 * padding, height: frame.height + 2 * padding) let path = UIBezierPath(rect: borderFrame).cgPath @@ -405,7 +526,7 @@ open class PDFGenerator { // Draw grid - let tableFrame = CGRect(x: x, y: contentHeight + maxHeaderHeight() + headerSpace, width: totalWidth, height: y - contentHeight) + let tableFrame = CGRect(x: x, y: contentHeight + maxHeaderHeight() + headerSpace, width: totalWidth, height: totalHeight) context?.stroke(tableFrame) // Change colors to draw fill instead of stroke @@ -426,11 +547,8 @@ open class PDFGenerator { // Draw horizontal lines var lineY: CGFloat = 0 - for (rowIdx, _) in frames.dropLast().enumerated() { - var maxHeight: CGFloat = 0 - for col in frames[rowIdx] { - maxHeight = max(maxHeight, col.height) - } + for (rowIdx, frame) in framesInThisPage.dropLast().enumerated() { + var maxHeight: CGFloat = frame.reduce(0) { max($0, $1.height) } lineY += maxHeight + 2 * margin + 2 * padding @@ -441,24 +559,133 @@ open class PDFGenerator { context?.drawPath(using: .fill) } - contentHeight = tableFrame.maxY - maxHeaderHeight() - headerSpace + if !dataInNewPage.isEmpty { + generateNewPage() + drawTable(container, data: dataInNewPage, alignments: alignmentsInNewPage, relativeColumnWidth: relativeColumnWidth, padding: padding, margin: margin, textColor: textColor, lineColor: lineColor, lineWidth: lineWidth, drawCellBounds: drawCellBounds, textFont: textFont) + } + else { + contentHeight = tableFrame.maxY - maxHeaderHeight() - headerSpace + } } - fileprivate func calculateCellFrame(_ origin: CGPoint, width: CGFloat, text: NSString, alignment: TableCellAlignment, attributes: [String: AnyObject]) -> CGRect { - let rect = CGRect(origin: origin, size: CGSize(width: width, height: 0)) + fileprivate func calculateCellFrame(_ origin: CGPoint, width: CGFloat, text: NSAttributedString, alignment: TableCellAlignment) -> CGRect { + let textMaxHeight = pageBounds.height - maxHeaderHeight() - headerSpace - maxFooterHeight() - footerSpace - contentHeight + // The height is not enough + if (textMaxHeight <= 0) { + return CGRect.infinite + } + let frame: CGRect = CGRect(x: origin.x, y: origin.y, width: width, height: textMaxHeight) - let size = text.size(attributes: attributes) + var currentRange = CFRange(location: 0, length: 0) + let (_, drawnSize) = calculateTextFrameAndDrawnSizeInOnePage(frame: frame, text: text, currentRange: currentRange) let x: CGFloat = { switch alignment.normalizeHorizontal { case .center: - return rect.midX - size.width / 2 + return origin.x + width / 2 - drawnSize.width / 2 case .right: - return rect.maxX - size.width + return origin.x + width - drawnSize.width + default: + return origin.x + } + }() + + return CGRect(origin: CGPoint(x: x, y: origin.y), size: CGSize(width: drawnSize.width, height: drawnSize.height)) + } + + fileprivate func calculateTextFrameAndDrawnSizeInOnePage(frame: CGRect, text: CFAttributedString, currentRange: CFRange) -> (CTFrame, CGSize) { + + let framesetter = CTFramesetterCreateWithAttributedString(text) + let framePath = UIBezierPath(rect: frame).cgPath + + // Get the frame that will do the rendering. + // The currentRange variable specifies only the starting point. The framesetter + // lays out as much text as will fit into the frame. + let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil) + + // Update the current range based on what was drawn. + let visibleRange = CTFrameGetVisibleStringRange(frameRef) + + // Update last drawn frame + let constraintSize = frame.size + let drawnSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, visibleRange, nil, constraintSize, nil) + + return (frameRef, drawnSize) + } + + fileprivate func calculateTextFrameAndDrawnSizeInOnePage(_ container: Container, text: CFAttributedString, currentRange: CFRange, textMaxWidth: CGFloat) -> (CTFrame, CGSize) { + let textMaxWidth = (textMaxWidth > 0) ? textMaxWidth : pageBounds.width - 2 * pageMargin - indentation[container.normalize]! + let textMaxHeight: CGFloat = { + if container.isHeader { + return pageBounds.height - headerHeight[container]! + } else if container.isFooter { + return footerMargin + } else { + return pageBounds.height - maxHeaderHeight() - headerSpace - maxFooterHeight() - footerSpace - contentHeight + } + }() + + // Create a path object to enclose the text. + let x: CGFloat = { + switch container { + case .headerLeft, .contentLeft, .footerLeft: + return pageMargin + indentation[container.normalize]! + case .headerCenter, .contentCenter, .footerCenter: + return pageBounds.midX - textMaxWidth / 2 + case .headerRight, .contentRight, .footerRight: + return pageBounds.width - pageMargin - textMaxWidth + default: + return 0 + } + }() + + let frame: CGRect = { + if container.isHeader { + return CGRect(x: x, y: 0, width: textMaxWidth, height: textMaxHeight) + } else if container.isFooter { + return CGRect(x: x, y: footerHeight[container]!, width: textMaxWidth, height: textMaxHeight) + } else { + return CGRect(x: x, y: maxFooterHeight() + footerSpace, width: textMaxWidth, height: textMaxHeight) + } + }() + + return calculateTextFrameAndDrawnSizeInOnePage(frame: frame, text: text, currentRange: currentRange) + } + + fileprivate func calculateImageCaptionSize(_ container: Container, image: UIImage, size: CGSize, caption: NSAttributedString, sizeFit: ImageSizeFit) -> (CGSize, CGSize) { + /* calculate the aspect size of image */ + var size = (size == CGSize.zero) ? image.size : size + if container.isHeader || container.isFooter { + size = CGSize(width: PDFGenerator.headerImageHeight, height: PDFGenerator.headerImageHeight) + } + + let maxWidth = min(size.width, contentSize.width - indentation[container.normalize]!) + let maxHeight = min(size.height, contentSize.height - contentHeight) + + let wFactor = image.size.width / maxWidth + let hFactor = image.size.height / maxHeight + let factor: CGFloat = { + switch sizeFit { + case .width: + return wFactor + case .height: + return hFactor + case .widthHeight: + return max(wFactor, hFactor) default: - return rect.minX + return max(wFactor, hFactor) } }() - return CGRect(origin: CGPoint(x: x, y: origin.y), size: size) + + let imageSize = CGSize(width: image.size.width / factor, height: image.size.height / factor) + + var (captionText, captionSize) = (NSAttributedString(), CGSize.zero) + if caption.length > 0 { + let currentText = CFAttributedStringCreateCopy(nil, caption as CFAttributedString) + var currentRange = CFRange(location: 0, length: 0) + (_, captionSize) = calculateTextFrameAndDrawnSizeInOnePage(container, text: currentText!, currentRange: currentRange, textMaxWidth: imageSize.width) + } + + return (imageSize, CGSize(width: imageSize.width, height: captionSize.height)) } // MARK: - Tools @@ -481,14 +708,47 @@ open class PDFGenerator { return max(pageMargin, max(footerHeight[.footerLeft]!, max(footerHeight[.footerCenter]!, footerHeight[.footerRight]!))) } + fileprivate func generateDefaultTextAttributes(_ container: Container, font: UIFont, spacing: CGFloat) -> [String: NSObject] { + let paragraphStyle = NSMutableParagraphStyle() + switch container { + case .headerLeft, .contentLeft, .footerLeft: + paragraphStyle.alignment = .left + case .headerCenter, .contentCenter, .footerCenter: + paragraphStyle.alignment = .center + case .headerRight, .contentRight, .footerRight: + paragraphStyle.alignment = .right + default: + paragraphStyle.alignment = .left + } + + paragraphStyle.lineSpacing = spacing + + return [ + NSFontAttributeName: font, + NSParagraphStyleAttributeName: paragraphStyle + ] + } + fileprivate func renderHeaderFooter(_ repeated: Bool = false) { resetHeaderFooterHeight() + if paginationContainer != .none { + renderCommand(paginationContainer, command: .addText(text: String(page), lineSpacing: 1.0)) + } + for (container, command) in headerFooterCommands { renderCommand(container, command: command) } } + fileprivate func generateNewPage() { + UIGraphicsBeginPDFPageWithInfo(pageBounds, nil) + contentHeight = 0 + page += 1 + + renderHeaderFooter() + } + fileprivate func renderCommand(_ container: Container, command: Command) { switch command { case let .addText(text, spacing): @@ -497,8 +757,11 @@ open class PDFGenerator { case let .addAttributedText(text): drawAttributedText(container, text: text) break - case let .addImage(image, size): - drawImage(container, image: image, size: size) + case let .addImage(image, size, caption, sizeFit): + drawImage(container, image: image, size: size, caption: caption, sizeFit: sizeFit) + break + case let .addImagesInRow(images, captions, spacing): + drawImagesInRow(container, images: images, captions: captions, spacing: spacing) break case let .addSpace(space): if container.isHeader { @@ -511,8 +774,8 @@ open class PDFGenerator { break case let .addLineSeparator(width, color): drawLineSeparator(container, thickness: width, color: color) - case let .addTable(data, alignment, relativeWidth, padding, margin, textColor, lineColor, lineWidth, drawCellBounds): - drawTable(container, data: data, alignments: alignment, relativeColumnWidth: relativeWidth, padding: padding, margin: margin, textColor: textColor, lineColor: lineColor, lineWidth: lineWidth, drawCellBounds: drawCellBounds) + case let .addTable(data, alignment, relativeWidth, padding, margin, textColor, lineColor, lineWidth, drawCellBounds, textFont): + drawTable(container, data: data, alignments: alignment, relativeColumnWidth: relativeWidth, padding: padding, margin: margin, textColor: textColor, lineColor: lineColor, lineWidth: lineWidth, drawCellBounds: drawCellBounds, textFont: textFont) case let .setIndentation(value): indentation[container.normalize] = value case let .setOffset(value): @@ -524,6 +787,40 @@ open class PDFGenerator { contentHeight = value } break + case let .createNewPage(): + generateNewPage() + } + } + + fileprivate func generateDocumentInfo() -> [AnyHashable : Any] { + var documentInfo: [AnyHashable : Any] = [ + kCGPDFContextTitle as String: info.title, + kCGPDFContextAuthor as String: info.author, + kCGPDFContextSubject as String: info.subject, + kCGPDFContextKeywords as String: info.keywords, + kCGPDFContextAllowsPrinting as String: info.allowsPrinting, + kCGPDFContextAllowsCopying as String: info.allowsCopying] + + var creator = "" + if let bundleName = Bundle.main.infoDictionary?["CFBundleName"] as? String { + creator += bundleName + + if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + creator += " v" + version + } } + if !creator.isEmpty { + documentInfo[kCGPDFContextCreator as String] = creator + } + + if let ownerPassword = info.ownerPassword { + documentInfo[kCGPDFContextOwnerPassword as String] = ownerPassword + } + + if let userPassword = info.userPassword { + documentInfo[kCGPDFContextUserPassword as String] = userPassword + } + + return documentInfo } } diff --git a/TPPDF/Classes/PDFInfo.swift b/TPPDF/Classes/PDFInfo.swift new file mode 100644 index 00000000..5ac235ae --- /dev/null +++ b/TPPDF/Classes/PDFInfo.swift @@ -0,0 +1,18 @@ +// +// PDFInfo.swift +// Pods +// +// Created by Zheng-Xiang Ke on 2016/12/15. +// +// + +public struct PDFInfo { + public var title = "kf99916/TPPDF" + public var author = "kf99916/TPPDF" + public var subject = "https://github.com/kf99916/TPPDF" + public var keywords = ["tppdf", "pdf", "kf99916"] + public var ownerPassword: String? + public var userPassword: String? + public var allowsPrinting = true + public var allowsCopying = true +} diff --git a/apps/Hikingbook.png b/apps/Hikingbook.png new file mode 100644 index 00000000..1ee96ebf Binary files /dev/null and b/apps/Hikingbook.png differ