├── .DS_Store ├── BLLaunch Screen.storyboard ├── BarcodeLabel.entitlements ├── BarcodeLabel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ ├── chinpinboo.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings │ │ └── technoriver.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── BarcodeLabel (iOS).xcscheme │ │ └── BarcodeLabel (macOS).xcscheme └── xcuserdata │ ├── chinpinboo.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── technoriver.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── README.md ├── Shared ├── .DS_Store ├── Assets.xcassets │ ├── .DS_Store │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon-BarcodeLabel.appiconset │ │ ├── Contents.json │ │ ├── Icon-20.png │ │ ├── Icon-20@3x.png │ │ ├── Icon-29.png │ │ ├── Icon-29@2x-1.png │ │ ├── Icon-29@2x.png │ │ ├── Icon-29@3x.png │ │ ├── Icon-40.png │ │ ├── Icon-40@2x-1.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x-1.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-41.png │ │ ├── Icon-42.png │ │ ├── Icon-512@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ └── Icon-83.5@2x.png │ ├── Contents.json │ └── LabelImage.imageset │ │ ├── 15415.jpg │ │ └── Contents.json ├── BarcodeLabel │ ├── Barcodes │ │ ├── Code39.swift │ │ ├── Industrial2of5.swift │ │ └── POSTNET.swift │ ├── Fonts │ │ ├── ConnectCode39_S3.otf │ │ ├── ConnectCodeIND2of5_S3.otf │ │ └── ConnectCodeIPostnet.otf │ ├── Models │ │ ├── AppSettings.swift │ │ ├── LabelDocument.swift │ │ ├── LabelTemplates.swift │ │ ├── OptionSettings.swift │ │ └── PageSettings.swift │ ├── Shapes │ │ ├── BarcodeX.swift │ │ ├── EllipseX.swift │ │ ├── ImageX.swift │ │ ├── RectangleX.swift │ │ ├── ShapeX.swift │ │ ├── ShapesX.swift │ │ └── TextX.swift │ ├── Util │ │ └── LabelAction.swift │ ├── ViewModels │ │ ├── BarcodePropertiesViewModel.swift │ │ ├── BaseViewModel.swift │ │ ├── FileViewModel.swift │ │ ├── ImagePropertiesViewModel.swift │ │ ├── ObjectBaseViewModel.swift │ │ ├── PreviewViewModel.swift │ │ ├── ShapePropertiesViewModel.swift │ │ ├── TemplateViewModel.swift │ │ └── TextPropertiesViewModel.swift │ └── Views │ │ ├── AlertWrapper.swift │ │ ├── BarcodePropertiesViewX.swift │ │ ├── DimensionsPropertiesViewX.swift │ │ ├── FilesViewX.swift │ │ ├── ImagePickerX.swift │ │ ├── ImagePropertiesViewX.swift │ │ ├── LabelDesignViewX.swift │ │ ├── LabelMainViewX.swift │ │ ├── LabelToolbarViewX.swift │ │ ├── LabelViewX.swift │ │ ├── MainPropertiesViewX.swift │ │ ├── MainSheetViewX.swift │ │ ├── OptionsPrintToolbarViewX.swift │ │ ├── PageToolbarViewX.swift │ │ ├── PreviewViewX.swift │ │ ├── RulerHViewX.swift │ │ ├── RulerVViewX.swift │ │ ├── ShapePropertiesViewX.swift │ │ ├── ShapeViewX.swift │ │ ├── TemplateLabelViewX.swift │ │ ├── TemplateViewX.swift │ │ └── TextPropertiesViewX.swift ├── BarcodeLabelApp.swift ├── IMG_20200419_102525.jpg └── test.jpg ├── Tests iOS ├── Info.plist └── Tests_iOS.swift ├── Tests macOS ├── Info.plist └── Tests_macOS.swift ├── iOS └── Info.plist └── macOS ├── Info.plist └── macOS.entitlements /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/.DS_Store -------------------------------------------------------------------------------- /BLLaunch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /BarcodeLabel.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.assets.pictures.read-write 8 | 9 | com.apple.security.device.camera 10 | 11 | com.apple.security.files.user-selected.read-write 12 | 13 | com.apple.security.network.client 14 | 15 | com.apple.security.personal-information.photos-library 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/xcuserdata/chinpinboo.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/BarcodeLabel.xcodeproj/project.xcworkspace/xcuserdata/chinpinboo.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/xcuserdata/chinpinboo.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | ShowSharedSchemesAutomaticallyEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/project.xcworkspace/xcuserdata/technoriver.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/BarcodeLabel.xcodeproj/project.xcworkspace/xcuserdata/technoriver.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/xcshareddata/xcschemes/BarcodeLabel (iOS).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 54 | 56 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/xcshareddata/xcschemes/BarcodeLabel (macOS).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/xcuserdata/chinpinboo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/xcuserdata/chinpinboo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BarcodeLabel (iOS).xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | BarcodeLabel (macOS).xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 01626BCC25C163D7001D69F4 21 | 22 | primary 23 | 24 | 25 | 01626BDC25C163D7001D69F4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /BarcodeLabel.xcodeproj/xcuserdata/technoriver.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SketchEffects (iOS).xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | SketchEffects (macOS).xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI-WYSIWYG-Draw 2 | 3 | This source code explores how to use SwiftUI for developing a What-You-See-Is-What-You-Get (WYSIWYG) vector drawing app. A WYSIWYG vector drawing app can be thought of as one that renders different objects such as a rectangle, an ellipse, a text, or other shapes on a canvas; 4 | the moving of objects around a canvas by dragging; and changing object properties by tapping on objects. 5 | This is illustrated in the screenshot of an iOS app below. 6 | Specifically, this article explores the use of a SwiftUI View as the "Drawing Canvas" instead of using a Core Graphics Canvas or a SwiftUI Canvas. 7 | 8 | 9 | 10 | ## Some points on the design 11 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 12 | 13 | ## The proof of concept app 14 | Barcode & Label 15 | 16 | ## Why use SwiftUI View as the drawing canvas? 17 | 18 | When developing a vector drawing app in the Apple ecosystem, things that come immediately onto the mind are Core Graphics or SwiftUI Canvas. Both are extremely fast, easy to use, and provide a canvas for us to draw on. Naturally, both are good choices as the canvas of a drawing app. However, when the drawing app requires WYSIWYG behavior, a Swift developer realizes that handling of object interactions such as drag, move, and resize, requires the use of Apple's Gestures and Events. 19 | 20 | This makes it hard for a Swift developer to ignore the use of a SwiftUI View as the "Drawing Canvas". This is because all Apple's Gestures and Events are directly supported by a SwiftUI View. If one is to take a step back and think about it, a SwiftUI View is designed by Apple for User Interface (UI) development, and such a View already naturally supports all the behavior required by a WYSIWYG app: rendering views and objects, and supporting gestures and events. The use of a SwiftUI View as the "Drawing Canvas" also does not prevent us from using Core Graphics, SwiftUI Canvas, or even Metal for rendering the underlying object that requires special treatment, as all three can be represented as a SwiftUI View easily. 21 | 22 | ## Compiling the Source 23 | 24 | Prerequisites 25 | 1. Xcode 13 26 | 2. iOS 15 27 | 28 | Build 29 | 1. Download the Source 30 | 2. Launch Xcode and load BarcodeLabel.xcodeproj 31 | 3. Build and run on iPhone Simulator or Device 32 | 33 | 34 | ## Sister Project 35 | 36 | [Node based Image/Video Compositing on Mobile](https://github.com/Misfits-Rebels-Outcasts/Nodef) 37 | 38 | -------------------------------------------------------------------------------- /Shared/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/.DS_Store -------------------------------------------------------------------------------- /Shared/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "Icon-40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-40@3x-1.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "Icon-60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "Icon-41.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "Icon-29@2x-1.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-42.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "Icon-40@2x-1.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "Icon-76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-83.5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "Icon-512@2x.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-20.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-20@3x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@2x-1.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@3x-1.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-41.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-42.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-512@2x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/AppIcon-BarcodeLabel.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Shared/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/LabelImage.imageset/15415.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/Assets.xcassets/LabelImage.imageset/15415.jpg -------------------------------------------------------------------------------- /Shared/Assets.xcassets/LabelImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "15415.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Barcodes/Code39.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | import Foundation 18 | 19 | extension String { 20 | subscript (index: Int) -> Character { 21 | let charIndex = self.index(self.startIndex, offsetBy: index) 22 | return self[charIndex] 23 | } 24 | 25 | subscript (range: Range) -> Substring { 26 | let startIndex = self.index(self.startIndex, offsetBy: range.startIndex) 27 | let stopIndex = self.index(self.startIndex, offsetBy: range.startIndex + range.count) 28 | return self[startIndex.. Int { 52 | 53 | guard let cVal = CODE39MAP.firstIndex(of: "\(inputChar)") else { 54 | return -1 55 | } 56 | 57 | return cVal; 58 | } 59 | 60 | func filterInput (_ inputData:String) -> String { 61 | var retStr="" 62 | inputData.forEach { char in 63 | if (CODE39MAP.contains("\(char)")) 64 | { 65 | retStr = retStr + "\(char)" 66 | } 67 | } 68 | return retStr 69 | } 70 | 71 | public mutating func encode () -> String { 72 | 73 | var cd="" 74 | var result="" 75 | var filtereddata = filterInput(inputData) 76 | let filteredlength=filtereddata.count 77 | 78 | if (checkDigit==1) 79 | { 80 | if (filteredlength > 254) 81 | { 82 | filtereddata=String(filtereddata.prefix(254)) 83 | } 84 | cd=generateCheckDigit(filtereddata); 85 | } 86 | else 87 | { 88 | if (filteredlength > 255) 89 | { 90 | filtereddata=String(filtereddata.prefix(255)) 91 | } 92 | } 93 | result="*"+filtereddata+cd+"*" 94 | humanReadableText=result 95 | return result 96 | 97 | 98 | } 99 | 100 | public func getHumanReadableText()->String{ 101 | return humanReadableText 102 | } 103 | 104 | func getCode39Character(_ inputdecimal:Int) -> String 105 | { 106 | let str=CODE39MAP[inputdecimal] 107 | return str 108 | } 109 | 110 | func generateCheckDigit(_ bdata:String)->String 111 | { 112 | var datalength=0 113 | var sum = 0 114 | var result = -1 115 | var strResult="" 116 | 117 | datalength=bdata.count 118 | //guard the for loop 119 | if datalength>0 120 | { 121 | for x in 0...datalength-1 122 | { 123 | let barcodechar = bdata[bdata.index(bdata.startIndex, offsetBy: x)] 124 | sum = sum + getCode39Value(barcodechar); 125 | } 126 | } 127 | result=sum % 43 128 | strResult=getCode39Character(result) 129 | 130 | return strResult 131 | 132 | } 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Barcodes/Industrial2of5.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | 18 | import Foundation 19 | 20 | public struct Industrial2of5 { 21 | 22 | public var inputData="" 23 | public var checkDigit=1 24 | public var humanReadableText="" 25 | 26 | init() { 27 | self.inputData="12345678" 28 | self.checkDigit=1 29 | } 30 | 31 | public init(_ inputData:String,_ checkDigit:Int) { 32 | self.inputData=inputData 33 | self.checkDigit=checkDigit 34 | } 35 | 36 | func filterInput (_ inputData:String) -> String { 37 | var retStr="" 38 | 39 | inputData.forEach { char in 40 | if let uchar = char.asciiValue { 41 | let barcodevalue=Int(uchar) 42 | 43 | if barcodevalue <= 57 && barcodevalue >= 48 44 | { 45 | retStr = retStr + "\(char)" 46 | } 47 | } 48 | } 49 | 50 | return retStr 51 | } 52 | 53 | public mutating func encode () -> String { 54 | 55 | var cd="" 56 | var result="" 57 | var filtereddata = filterInput(inputData) 58 | let filteredlength=filtereddata.count 59 | 60 | if (checkDigit == 1) 61 | { 62 | if (filteredlength > 254) 63 | { 64 | filtereddata=String(filtereddata.prefix(254)) 65 | } 66 | cd=generateCheckDigit(filtereddata); 67 | } 68 | else{ 69 | 70 | if (filteredlength > 255) 71 | { 72 | filtereddata=String(filtereddata.prefix(255)) 73 | } 74 | 75 | 76 | } 77 | filtereddata = filtereddata + cd 78 | humanReadableText=filtereddata 79 | 80 | result="{"+filtereddata+"}" 81 | 82 | 83 | return result 84 | } 85 | 86 | public func getHumanReadableText()->String{ 87 | return humanReadableText 88 | } 89 | 90 | func generateCheckDigit(_ bdata:String)->String 91 | { 92 | var datalength=0 93 | var lastcharpos = 0 94 | var result=0 95 | var strResult="" 96 | var barcodechar=0 97 | var barcodevalue=0 98 | var toggle=1 99 | var sum=0 100 | 101 | datalength=bdata.count 102 | lastcharpos=datalength-1 103 | 104 | var x=lastcharpos 105 | while (x>=0) 106 | { 107 | barcodechar=(Int)(bdata[x].asciiValue!) 108 | barcodevalue=barcodechar-48 109 | if toggle == 1 110 | { 111 | sum += (barcodevalue*3); 112 | toggle = 0; 113 | } 114 | else 115 | { 116 | sum += barcodevalue; 117 | toggle = 1; 118 | } 119 | 120 | x=x-1 121 | } 122 | 123 | if sum % 10 == 0 124 | { 125 | result=0+48 126 | } 127 | else{ 128 | result = (10 - (sum % 10)) + 48 129 | } 130 | 131 | strResult=strResult+String(UnicodeScalar(result)!) 132 | return strResult 133 | 134 | } 135 | 136 | 137 | } 138 | 139 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Barcodes/POSTNET.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | import Foundation 18 | 19 | public struct POSTNET { 20 | 21 | 22 | public var inputData="" 23 | public var humanReadableText="" 24 | 25 | init() { 26 | self.inputData="12345678" 27 | } 28 | 29 | public init(_ inputData:String) { 30 | self.inputData=inputData 31 | } 32 | 33 | func getPOSTNETValue(_ inputChar:Character) -> Int { 34 | return (Int)(inputChar.asciiValue!) - 48 35 | } 36 | 37 | func filterInput (_ inputData:String) -> String { 38 | var retStr="" 39 | 40 | inputData.forEach { char in 41 | if let uchar = char.asciiValue { 42 | let barcodevalue=Int(uchar) 43 | 44 | if barcodevalue <= 57 && barcodevalue >= 48 45 | { 46 | retStr = retStr + "\(char)" 47 | } 48 | } 49 | } 50 | 51 | return retStr 52 | 53 | } 54 | 55 | public mutating func encode () -> String { 56 | 57 | var cd="" 58 | var result="" 59 | var filtereddata = filterInput(inputData) 60 | let filteredlength=filtereddata.count 61 | 62 | if (filteredlength > 11) 63 | { 64 | filtereddata=String(filtereddata.prefix(11)) 65 | } 66 | 67 | cd=generateCheckDigit(filtereddata); 68 | 69 | humanReadableText=filtereddata+cd 70 | result="{"+filtereddata+cd+"}" 71 | return result 72 | 73 | 74 | } 75 | 76 | public func getHumanReadableText()->String{ 77 | return humanReadableText 78 | } 79 | 80 | func getPOSTNETCharacter(_ inputdecimal:Int) -> String 81 | { 82 | return String(UnicodeScalar(inputdecimal+48)!) 83 | } 84 | 85 | func generateCheckDigit(_ bdata:String)->String 86 | { 87 | var datalength=0 88 | var sum = 0 89 | var result = -1 90 | var strResult="" 91 | 92 | datalength=bdata.count 93 | if datalength>0 94 | { 95 | for x in 0...datalength-1 96 | { 97 | let barcodechar = bdata[x] 98 | sum = sum + getPOSTNETValue(barcodechar) 99 | } 100 | } 101 | 102 | result=sum % 10 103 | if result != 0 104 | { 105 | result = 10 - result 106 | } 107 | 108 | strResult=getPOSTNETCharacter(result) 109 | 110 | return strResult 111 | 112 | } 113 | 114 | 115 | } 116 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Fonts/ConnectCode39_S3.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/BarcodeLabel/Fonts/ConnectCode39_S3.otf -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Fonts/ConnectCodeIND2of5_S3.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/BarcodeLabel/Fonts/ConnectCodeIND2of5_S3.otf -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Fonts/ConnectCodeIPostnet.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/BarcodeLabel/Fonts/ConnectCodeIPostnet.otf -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Models/AppSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppSettings.swift 3 | // 4 | // 5 | // Created by on 15/3/22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class AppSettings: ObservableObject, Identifiable, Equatable { 12 | 13 | static func == (lhs: AppSettings, rhs: AppSettings) -> Bool { 14 | return lhs.id==rhs.id 15 | } 16 | 17 | var id = UUID() 18 | 19 | @Published var dpi: Double = 300.0 20 | @Published var dpiScale: Double = 300.0/72.0 21 | @Published var zoomFactor: Double = 1.0 22 | @Published var units: String = "Inches" 23 | @Published var zoomingOrScrollX: String = "scroll" //zoomIn, zoomOut, scroll 24 | @Published var zoomingOrScrollY: String = "scroll" //zoomIn, zoomOut, scroll 25 | 26 | 27 | 28 | 29 | init(_ dpi: Double, _ zoomFactor: Double) { 30 | self.dpi=dpi 31 | self.dpiScale=dpi/72.0 32 | self.zoomFactor=zoomFactor 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Models/LabelDocument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageDocument.swift 3 | // ImportExport 4 | // 5 | // Created by Aaron Wright on 8/27/20. 6 | // 7 | 8 | import SwiftUI 9 | import UniformTypeIdentifiers 10 | 11 | struct LabelDocument: FileDocument { 12 | 13 | static var readableContentTypes: [UTType] { [.json] } 14 | 15 | var message: String 16 | 17 | init(message: String) { 18 | self.message = message 19 | } 20 | 21 | init(configuration: ReadConfiguration) throws { 22 | guard let data = configuration.file.regularFileContents, 23 | let string = String(data: data, encoding: .utf8) 24 | else { 25 | throw CocoaError(.fileReadCorruptFile) 26 | } 27 | message = string 28 | } 29 | 30 | func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { 31 | return FileWrapper(regularFileWithContents: message.data(using: .utf8)!) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Models/LabelTemplates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelTemplatesModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | 8 | 9 | var labelTemplatesAll = [ 10 | 11 | ["Standard SA4001","Labels","Standard","Address Label (A4) - 8x3","iso-a4","8.268","11.693","2.520","1.333","0.104","0.000","8","3","0.250","0.541"], 12 | ["Standard SA4002","Labels","Standard","Address Label (A4) - 7x3","iso-a4","8.268","11.693","2.500","1.500","0.100","0.000","7","3","0.283","0.625"], 13 | ["Standard SA4003","Labels","Standard","Address Label (A4) - 6x3","iso-a4","8.268","11.693","2.500","1.833","0.100","0.000","6","3","0.283","0.375"], 14 | ["Standard SA4004","Labels","Standard","Address Label (A4) - 8x2","iso-a4","8.268","11.693","3.900","1.333","0.100","0.000","8","2","0.184","0.541"], 15 | ["Standard SA4005","Labels","Standard","Address Label (A4) - 7x2","iso-a4","8.268","11.693","3.900","1.500","0.100","0.000","7","2","0.184","0.625"], 16 | ["Standard SA4006","Labels","Standard","Address Label (A4) - 4x2","iso-a4","8.268","11.693","3.900","2.667","0.100","0.000","4","2","0.184","0.542"], 17 | 18 | ["Standard SLE001","Labels","Standard","Address Label (Letter) - 10x2","iso-letter","8.500","11.000","4.000","1.000","0.189","0.000","10","2","0.156","0.500"], 19 | ["Standard SLE002","Labels","Standard","Address/Shipping (Letter) - 3x2","iso-letter","8.500","11.000","4.000","3.333","0.188","0.000","3","2","0.156","0.500"], 20 | ["Standard SLE003","Labels","Standard","Address/Shipping (Letter) - 5x2","iso-letter","8.500","11.000","4.000","2.000","0.188","0.000","5","2","0.156","0.500"], 21 | ["Standard SLE004","Labels","Standard","Address Label (Letter) - 7x2","iso-letter","8.500","11.000","4.000","1.333","0.188","0.000","7","2","0.156","0.833"], 22 | ["Standard SLE005","Labels","Standard","Address Label (Letter) - 10x3","iso-letter","8.500","11.000","2.625","1.000","0.125","0.000","10","3","0.188","0.500"], 23 | 24 | ] 25 | var labelTemplates = labelTemplatesAll.filter { 26 | $0[1].contains("Labels") && $0[2].contains("Standard") 27 | } 28 | 29 | var labelTemplatesCount = 11 30 | 31 | var categories = ["Labels"] 32 | //var categories = ["Labels","Envelopes","Cards & Tags","Papers"] 33 | 34 | var vendorsCards = ["Standard"] 35 | 36 | var vendorsLabels = ["Standard"] 37 | 38 | var vendors = vendorsLabels 39 | 40 | var fontNames = ["Helvetica","Helvetica Bold","American Typewriter Bold","Arial","Arial Bold","Arial Italic"] 41 | 42 | var barcodeFontNames = ["CCode39_S3","CCodeIPostnet","CCodeIND2of5_S3", "Arial"] 43 | 44 | var barcodeNames = ["Code 39","Industrial 2 of 5","POSTNET"] 45 | 46 | var measurementUnits = ["Inches","Centimeters"] 47 | 48 | var orientation = ["Portrait","Landscape"] 49 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Models/OptionSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by on 17/3/22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class OptionSettings: ObservableObject, Identifiable, Equatable { 12 | 13 | static func == (lhs: OptionSettings, rhs: OptionSettings) -> Bool { 14 | return lhs.id==rhs.id 15 | } 16 | 17 | var id = UUID() 18 | 19 | @Published var action: String = "" 20 | @Published var showPropertiesView: Int = 0 21 | @Published var showPropertiesSheet: Bool = false 22 | @Published var previewBorderWidth: Double = 4.0 23 | @Published var showAlert = false //display aleart for entering label name the textfield alert 24 | @Published var labelName: String = "" 25 | @Published var jsonLabelStringForSave: String = "" 26 | @Published var labelDocument: LabelDocument = LabelDocument(message: "") 27 | @Published var isExporting: Bool = false 28 | @Published var isImporting: Bool = false 29 | @Published var showingAlertMessage = false 30 | @Published var existingLabelExist = false 31 | @Published var enteredSaveFileName: String? // this is updated as the user types in the text field 32 | @Published var alertMessage: String? //"Label saved successfully." in AlertWrapper 33 | 34 | init(_ action: String) { 35 | self.action=action 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Models/PageSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageSettings.swift 3 | // 4 | // 5 | // Created by on 16/3/22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ILabel: Identifiable { 12 | var id = UUID() 13 | var label: Int 14 | var x:CGFloat 15 | var y:CGFloat 16 | } 17 | 18 | class PageSettings: Codable, ObservableObject, Identifiable, Equatable { 19 | 20 | static func == (lhs: PageSettings, rhs: PageSettings) -> Bool { 21 | return lhs.id==rhs.id 22 | } 23 | 24 | var id = UUID() 25 | 26 | //name,category,vendor,description,orientation 27 | @Published var name: String = "Standard SLE005" 28 | @Published var category: String = "Labels" 29 | @Published var vendor: String = "Standard" 30 | @Published var description: String = "Address Label (Letter) - 10x3" 31 | @Published var type: String = "iso-letter" 32 | 33 | @Published var pageWidth: Double = 8.5 34 | @Published var pageHeight: Double = 11.0 35 | @Published var leftMargin: Double = 0.188 36 | @Published var topMargin: Double = 0.5 37 | @Published var labelWidth: Double = 2.625 38 | @Published var labelHeight: Double = 1.0 39 | @Published var hSpace: Double = 0.125 40 | @Published var vSpace: Double = 0.0 41 | @Published var numRows: Int = 10 42 | @Published var numCols: Int = 3 43 | @Published var dpi: Double = 300.0//72.0 44 | @Published var labelList = [ILabel]() 45 | 46 | init() { 47 | generateLabels() 48 | } 49 | init(name: String, 50 | category: String, 51 | vendor: String, 52 | description: String, 53 | type: String, 54 | pageWidth: Double, 55 | pageHeight: Double, 56 | leftMargin: Double, 57 | topMargin: Double, 58 | labelWidth: Double, 59 | labelHeight: Double, 60 | hSpace: Double, 61 | vSpace: Double, 62 | numRows: Int, 63 | numCols: Int, 64 | dpi: Double) { 65 | 66 | self.name=name 67 | self.category=category 68 | self.vendor=vendor 69 | self.description=description 70 | self.type=type 71 | self.pageWidth=pageWidth 72 | self.pageHeight=pageHeight 73 | self.leftMargin=leftMargin 74 | self.topMargin=topMargin 75 | self.labelWidth=labelWidth 76 | self.labelHeight=labelHeight 77 | self.hSpace=hSpace 78 | self.vSpace=vSpace 79 | self.numRows=numRows 80 | self.numCols=numCols 81 | self.dpi=dpi 82 | 83 | generateLabels() 84 | } 85 | 86 | func generateLabels() 87 | { 88 | generateLabels(dpi:dpi) 89 | } 90 | 91 | func generateLabels(dpi :Double) 92 | { 93 | labelList = [ILabel]() 94 | var x = leftMargin * dpi 95 | var y = topMargin * dpi 96 | let adjustX = labelWidth / 2.0 * dpi 97 | let adjustY = labelHeight / 2.0 * dpi 98 | var count = 1 99 | //print ("Label:",numRows,":",numCols,":",pageWidth,":",pageHeight,":",labelWidth,":",labelHeight) 100 | 101 | for _ in 1...numRows { 102 | for _ in 1...numCols { 103 | labelList.append(ILabel(label: count, x: x + adjustX, y: y + adjustY)) 104 | count=count+1 105 | x = x + hSpace * dpi 106 | x = x + labelWidth * dpi 107 | } 108 | x = leftMargin * dpi 109 | y = y + vSpace * dpi 110 | y = y + labelHeight * dpi 111 | } 112 | } 113 | 114 | enum CodingKeys: String, CodingKey { 115 | case name 116 | case category 117 | case vendor 118 | case description 119 | case type 120 | case pageWidth 121 | case pageHeight 122 | case leftMargin 123 | case topMargin 124 | case labelWidth 125 | case labelHeight 126 | case hSpace 127 | case vSpace 128 | case numRows 129 | case numCols 130 | case dpi 131 | } 132 | 133 | required init(from decoder: Decoder) throws { 134 | let values = try decoder.container(keyedBy: CodingKeys.self) 135 | name = try values.decodeIfPresent(String.self, forKey: .name) ?? "Standard" 136 | category = try values.decodeIfPresent(String.self, forKey: .category) ?? "Labels" 137 | vendor = try values.decodeIfPresent(String.self, forKey: .vendor) ?? "Standard" 138 | description = try values.decodeIfPresent(String.self, forKey: .description) ?? "Address Label (Letter) - 10x3" 139 | type = try values.decodeIfPresent(String.self, forKey: .type) ?? "iso-letter" 140 | pageWidth = try values.decodeIfPresent(Double.self, forKey: .pageWidth) ?? 8.5 141 | pageHeight = try values.decodeIfPresent(Double.self, forKey: .pageHeight) ?? 11.0 142 | leftMargin = try values.decodeIfPresent(Double.self, forKey: .leftMargin) ?? 0.188 143 | topMargin = try values.decodeIfPresent(Double.self, forKey: .topMargin) ?? 0.5 144 | labelWidth = try values.decodeIfPresent(Double.self, forKey: .labelWidth) ?? 2.625 145 | labelHeight = try values.decodeIfPresent(Double.self, forKey: .labelHeight) ?? 1.0 146 | hSpace = try values.decodeIfPresent(Double.self, forKey: .hSpace) ?? 0.125 147 | vSpace = try values.decodeIfPresent(Double.self, forKey: .vSpace) ?? 0.0 148 | numRows = try values.decodeIfPresent(Int.self, forKey: .numRows) ?? 10 149 | numCols = try values.decodeIfPresent(Int.self, forKey: .numCols) ?? 3 150 | dpi = try values.decodeIfPresent(Double.self, forKey: .dpi) ?? 300.0 151 | } 152 | 153 | func encode(to encoder: Encoder) throws { 154 | var container = encoder.container(keyedBy: CodingKeys.self) 155 | try container.encode(name, forKey: .name) 156 | try container.encode(category, forKey: .category) 157 | try container.encode(vendor, forKey: .vendor) 158 | try container.encode(description, forKey: .description) 159 | try container.encode(type, forKey: .type) 160 | try container.encode(pageWidth, forKey: .pageWidth) 161 | try container.encode(pageHeight, forKey: .pageHeight) 162 | try container.encode(leftMargin, forKey: .leftMargin) 163 | try container.encode(topMargin, forKey: .topMargin) 164 | try container.encode(labelWidth, forKey: .labelWidth) 165 | try container.encode(labelHeight, forKey: .labelHeight) 166 | try container.encode(hSpace, forKey: .hSpace) 167 | try container.encode(vSpace, forKey: .vSpace) 168 | try container.encode(numRows, forKey: .numRows) 169 | try container.encode(numCols, forKey: .numCols) 170 | try container.encode(dpi, forKey: .dpi) 171 | } 172 | } 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/BarcodeX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | class BarcodeX: ShapeX { 14 | 15 | @Published var fontSize:CGFloat = 99.0 //18.0*300.0/72.0 16 | @Published var input:String = "1234" 17 | @Published var symbology:String = "Code 39" 18 | @Published var checkDigit:Bool = false 19 | @Published var barcode:String = "*1234*" 20 | @Published var humanReadableText:String = "*1234*" 21 | @Published var fontName:String = "CCode39_S3" 22 | @Published var textColor = Color.black 23 | @Published var horizontalTextAlignment:String = "Center" 24 | 25 | @Published var hrFontName:String = "Arial" 26 | @Published var hrTextColor = Color.black 27 | @Published var hrFontSize:CGFloat = 16.0*300.0/72.0 28 | 29 | init(_ dpi:Double, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 30 | 31 | super.init(dpi,"Barcode",location,size,canvasSize,isSelected) 32 | fontSize = 23.76 * dpi / 72.0 //99.0 gives the same size as other platforms 33 | hrFontSize = 16.0*dpi/72.0 34 | } 35 | 36 | enum CodingKeys : String, CodingKey { 37 | case input 38 | case symbology 39 | case checkDigit 40 | case barcode 41 | case humanReadableText 42 | 43 | case fontSize 44 | case fontName 45 | case textColor 46 | 47 | case hrFontSize 48 | case hrFontName 49 | case hrTextColor 50 | 51 | case horizontalTextAlignment 52 | } 53 | 54 | required init(from decoder: Decoder) throws { 55 | try super.init(from: decoder) 56 | let values = try decoder.container(keyedBy: CodingKeys.self) 57 | input = try values.decodeIfPresent(String.self, forKey: .input) ?? "1234" 58 | symbology = try values.decodeIfPresent(String.self, forKey: .symbology) ?? "Code 39" 59 | checkDigit = try values.decodeIfPresent(Bool.self, forKey: .checkDigit) ?? false 60 | 61 | barcode = try values.decodeIfPresent(String.self, forKey: .barcode) ?? "1234" 62 | humanReadableText = try values.decodeIfPresent(String.self, forKey: .humanReadableText) ?? "1234" 63 | 64 | fontSize = try values.decodeIfPresent(CGFloat.self, forKey: .fontSize) ?? 18.0*dpi/72.0 65 | fontName = try values.decodeIfPresent(String.self, forKey: .fontName) ?? "CCode39_S3" 66 | let textColorData = try values.decodeIfPresent(Data.self, forKey: .textColor) ?? nil 67 | if textColorData != nil 68 | { 69 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: textColorData!) 70 | textColor = Color(color!) 71 | } 72 | 73 | hrFontSize = try values.decodeIfPresent(CGFloat.self, forKey: .hrFontSize) ?? 16.0*300.0/72.0 74 | hrFontName = try values.decodeIfPresent(String.self, forKey: .hrFontName) ?? "Arial" 75 | let hrTextColorData = try values.decodeIfPresent(Data.self, forKey: .hrTextColor) ?? nil 76 | if hrTextColorData != nil 77 | { 78 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: hrTextColorData!) 79 | hrTextColor = Color(color!) 80 | } 81 | 82 | horizontalTextAlignment = try values.decodeIfPresent(String.self, forKey: .horizontalTextAlignment) ?? "Center" 83 | 84 | } 85 | 86 | override func encode(to encoder: Encoder) throws { 87 | try super.encode(to: encoder) 88 | var container = encoder.container(keyedBy: CodingKeys.self) 89 | 90 | try container.encode(input, forKey: .input) 91 | try container.encode(symbology, forKey: .symbology) 92 | try container.encode(checkDigit, forKey: .checkDigit) 93 | 94 | try container.encode(barcode, forKey: .barcode) 95 | try container.encode(humanReadableText, forKey: .humanReadableText) 96 | 97 | try container.encode(fontName, forKey: .fontName) 98 | try container.encode(fontSize, forKey: .fontSize) 99 | let convertedTextColor = UIColor(textColor) 100 | let textColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedTextColor, requiringSecureCoding: false) 101 | try container.encode(textColorData, forKey: .textColor) 102 | 103 | try container.encode(hrFontName, forKey: .hrFontName) 104 | try container.encode(hrFontSize, forKey: .hrFontSize) 105 | let hrconvertedTextColor = UIColor(hrTextColor) 106 | let hrTextColorData = try NSKeyedArchiver.archivedData(withRootObject: hrconvertedTextColor, requiringSecureCoding: false) 107 | try container.encode(hrTextColorData, forKey: .hrTextColor) 108 | 109 | try container.encode(horizontalTextAlignment, forKey: .horizontalTextAlignment) 110 | 111 | } 112 | 113 | override func view() -> AnyView { 114 | AnyView( 115 | VStack 116 | { 117 | HStack{ 118 | if horizontalTextAlignment == "Right" { Spacer() } 119 | Text(self.barcode) 120 | .lineLimit(1) 121 | .font(.custom(self.fontName, size: self.fontSize)) 122 | .foregroundColor(self.textColor) 123 | .fixedSize(horizontal: true, vertical: false) 124 | if horizontalTextAlignment == "Left" { Spacer() } 125 | } 126 | 127 | HStack{ 128 | if horizontalTextAlignment == "Right" { Spacer() } 129 | Text(self.humanReadableText) 130 | .lineLimit(1) 131 | .font(.custom(self.hrFontName, size: self.hrFontSize)) 132 | //.font(.custom("CCodeIND2of5_S3", size: self.hrFontSize)) 133 | .foregroundColor(self.hrTextColor) 134 | .fixedSize(horizontal: true, vertical: false) 135 | if horizontalTextAlignment == "Left" { Spacer() } 136 | } 137 | } 138 | .frame(width: self.size.width, height: self.size.height) 139 | .clipShape(Rectangle()) 140 | .position(self.location) 141 | ) 142 | } 143 | 144 | override func useElipsisIfSizeTooSmall() { 145 | 146 | } 147 | 148 | } 149 | 150 | 151 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/EllipseX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | class EllipseX: ShapeX { 13 | 14 | @Published var strokeWidth:Double = 0.25 * 300.0/72.0 15 | @Published var strokeColor = Color.black 16 | @Published var fillColor = Color.white 17 | 18 | init(_ dpi:Double, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 19 | super.init(dpi,"Ellipse",location,size,canvasSize,isSelected) 20 | strokeWidth = 0.25 * dpi/72.0 21 | } 22 | 23 | enum CodingKeys : String, CodingKey { 24 | case strokeWidth 25 | case strokeColor 26 | case fillColor 27 | } 28 | 29 | required init(from decoder: Decoder) throws { 30 | try super.init(from: decoder) 31 | let values = try decoder.container(keyedBy: CodingKeys.self) 32 | strokeWidth = try values.decodeIfPresent(Double.self, forKey: .strokeWidth) ?? 0.25 * dpi/72.0 33 | 34 | let strokeColorData = try values.decodeIfPresent(Data.self, forKey: .strokeColor) ?? nil 35 | if strokeColorData != nil 36 | { 37 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: strokeColorData!) 38 | strokeColor = Color(color!) 39 | } 40 | 41 | let fillColorData = try values.decodeIfPresent(Data.self, forKey: .fillColor) ?? nil 42 | if fillColorData != nil 43 | { 44 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: fillColorData!) 45 | fillColor = Color(color!) 46 | } 47 | } 48 | 49 | override func encode(to encoder: Encoder) throws { 50 | try super.encode(to: encoder) 51 | var container = encoder.container(keyedBy: CodingKeys.self) 52 | try container.encode(strokeWidth, forKey: .strokeWidth) 53 | 54 | let convertedStrokeColor = UIColor(strokeColor) 55 | let strokeColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedStrokeColor, requiringSecureCoding: false) 56 | try container.encode(strokeColorData, forKey: .strokeColor) 57 | 58 | let convertedFillColor = UIColor(fillColor) 59 | let fillColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedFillColor, requiringSecureCoding: false) 60 | try container.encode(fillColorData, forKey: .fillColor) 61 | } 62 | 63 | 64 | override func view() -> AnyView { 65 | AnyView( 66 | Ellipse() 67 | .stroke(strokeColor, lineWidth: strokeWidth) 68 | .background(Ellipse().fill(fillColor)) 69 | .frame(width: self.size.width, height: self.size.height) 70 | .position(self.location)) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/ImageX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | //image orientation 14 | //https://stackoverflow.com/questions/42098390/swift-png-image-being-saved-with-incorrect-orientation 15 | class ImageX: ShapeX { 16 | @Published var image: UIImage = UIImage(named: "LabelImage")! 17 | 18 | init(_ dpi:Double, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 19 | super.init(dpi,"Image",location,size,canvasSize,isSelected) 20 | print(location) 21 | print(size) 22 | print(canvasSize) 23 | print(isSelected) 24 | } 25 | 26 | enum CodingKeys : String, CodingKey { 27 | case image 28 | } 29 | 30 | required init(from decoder: Decoder) throws { 31 | try super.init(from: decoder) 32 | let container = try decoder.container(keyedBy: CodingKeys.self) 33 | 34 | let data = try container.decodeIfPresent(String.self, forKey: .image) ?? nil 35 | if data != nil 36 | { 37 | //https://stackoverflow.com/questions/11251340/convert-between-uiimage-and-base64-string 38 | self.image = convertBase64StringToImage(imageBase64String:data!) 39 | } 40 | } 41 | 42 | func convertImageToBase64String (img: UIImage) -> String { 43 | return img.jpegData(compressionQuality: 1)?.base64EncodedString() ?? "" 44 | } 45 | 46 | func convertBase64StringToImage (imageBase64String:String) -> UIImage { 47 | let imageData = Data(base64Encoded: imageBase64String) 48 | let image = UIImage(data: imageData!) 49 | return image! 50 | } 51 | 52 | override func encode(to encoder: Encoder) throws { 53 | try super.encode(to: encoder) 54 | var container = encoder.container(keyedBy: CodingKeys.self) 55 | try container.encode(convertImageToBase64String(img:image), forKey: .image) 56 | 57 | } 58 | 59 | override func view() -> AnyView { 60 | AnyView( 61 | Image(uiImage: image) 62 | .resizable() 63 | .frame(width: self.size.width, height: self.size.height) 64 | .position(self.location)) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/RectangleX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | class RectangleX: ShapeX { 14 | 15 | @Published var strokeWidth:Double = 0.25 * 300.0/72.0 16 | @Published var strokeColor = Color.black 17 | @Published var fillColor = Color.white 18 | 19 | init(_ dpi:Double, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 20 | super.init(dpi,"Rectangle",location,size,canvasSize,isSelected) 21 | strokeWidth = 0.25 * dpi/72.0 22 | } 23 | 24 | enum CodingKeys : String, CodingKey { 25 | case strokeWidth 26 | case strokeColor 27 | case fillColor 28 | } 29 | 30 | required init(from decoder: Decoder) throws { 31 | try super.init(from: decoder) 32 | let values = try decoder.container(keyedBy: CodingKeys.self) 33 | strokeWidth = try values.decodeIfPresent(Double.self, forKey: .strokeWidth) ?? 0.25 * dpi/72.0 34 | 35 | let strokeColorData = try values.decodeIfPresent(Data.self, forKey: .strokeColor) ?? nil 36 | if strokeColorData != nil 37 | { 38 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: strokeColorData!) 39 | strokeColor = Color(color!) 40 | } 41 | 42 | let fillColorData = try values.decodeIfPresent(Data.self, forKey: .fillColor) ?? nil 43 | if fillColorData != nil 44 | { 45 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: fillColorData!) 46 | fillColor = Color(color!) 47 | } 48 | } 49 | 50 | override func encode(to encoder: Encoder) throws { 51 | try super.encode(to: encoder) 52 | var container = encoder.container(keyedBy: CodingKeys.self) 53 | try container.encode(strokeWidth, forKey: .strokeWidth) 54 | 55 | let convertedStrokeColor = UIColor(strokeColor) 56 | let strokeColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedStrokeColor, requiringSecureCoding: false) 57 | try container.encode(strokeColorData, forKey: .strokeColor) 58 | 59 | let convertedFillColor = UIColor(fillColor) 60 | let fillColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedFillColor, requiringSecureCoding: false) 61 | try container.encode(fillColorData, forKey: .fillColor) 62 | } 63 | 64 | override func view() -> AnyView { 65 | AnyView( 66 | Rectangle() 67 | .stroke(strokeColor, lineWidth: strokeWidth) 68 | .background(Rectangle().fill(fillColor)) 69 | //.fill(Color.white) 70 | .frame(width: self.size.width, height: self.size.height) 71 | //.border(Color.black) 72 | .position(self.location) 73 | ) 74 | 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/ShapeX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | import SwiftUI 18 | 19 | class ShapeX: Codable, ObservableObject, Identifiable, Equatable { 20 | static func == (lhs: ShapeX, rhs: ShapeX) -> Bool { 21 | return lhs.id==rhs.id 22 | } 23 | 24 | var id = UUID() 25 | 26 | @Published var dpi = 300.0 27 | @Published var type: String = "" 28 | @Published var location: CGPoint = CGPoint(x: 50, y: 50) 29 | @Published var size: CGSize = CGSize(width: 100, height: 100) 30 | @Published var canvasSize: CGSize = CGSize(width: 1, height: 1) 31 | @Published var isSelected = false 32 | @Published var zIndex = 0.0 33 | 34 | var width: Float = 0.0 35 | var height: Float = 0.0 36 | 37 | init() 38 | { 39 | 40 | } 41 | init(_ dpi:Double, _ type: String, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 42 | self.dpi=dpi 43 | self.type=type 44 | self.location=location 45 | self.size=size 46 | self.canvasSize=canvasSize 47 | self.isSelected = isSelected 48 | } 49 | 50 | func useElipsisIfSizeTooSmall() { 51 | 52 | } 53 | 54 | func view() -> AnyView { 55 | AnyView(EmptyView()) 56 | } 57 | 58 | enum CodingKeys: String, CodingKey { 59 | case dpi 60 | case type 61 | case location 62 | case size 63 | case canvasSize 64 | case isSelected 65 | case zIndex 66 | } 67 | 68 | required init(from decoder: Decoder) throws { 69 | let values = try decoder.container(keyedBy: CodingKeys.self) 70 | dpi = try values.decodeIfPresent(Double.self, forKey: .dpi) ?? 300.0 71 | type = try values.decodeIfPresent(String.self, forKey: .type) ?? "" 72 | location = try values.decodeIfPresent(CGPoint.self, forKey: .location) ?? CGPoint(x: 50, y: 50) 73 | size = try values.decodeIfPresent(CGSize.self, forKey: .size) ?? CGSize(width: 100, height: 100) 74 | canvasSize = try values.decodeIfPresent(CGSize.self, forKey: .canvasSize) ?? CGSize(width: 1, height: 1) 75 | isSelected = try values.decodeIfPresent(Bool.self, forKey: .isSelected) ?? false 76 | zIndex = try values.decodeIfPresent(Double.self, forKey: .zIndex) ?? 0 77 | } 78 | 79 | func encode(to encoder: Encoder) throws { 80 | var container = encoder.container(keyedBy: CodingKeys.self) 81 | try container.encode(dpi, forKey: .dpi) 82 | try container.encode(type, forKey: .type) 83 | try container.encode(location, forKey: .location) 84 | try container.encode(size, forKey: .size) 85 | try container.encode(canvasSize, forKey: .canvasSize) 86 | try container.encode(isSelected, forKey: .isSelected) 87 | try container.encode(zIndex, forKey: .zIndex) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/ShapesX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | import SwiftUI 18 | 19 | class ShapesX: Codable, ObservableObject{ 20 | @Published var shapeList = [ShapeX]() 21 | 22 | init() { 23 | 24 | } 25 | 26 | func deSelectAll() { 27 | shapeList.forEach 28 | { 29 | $0.isSelected = false 30 | } 31 | } 32 | 33 | func add(shape: ShapeX) 34 | { 35 | shapeList.append(shape) 36 | } 37 | 38 | enum CodingKeys: String, CodingKey { 39 | case shapelist 40 | } 41 | 42 | enum ShapeTypeKey: CodingKey { 43 | case type 44 | } 45 | 46 | enum ShapeTypes: String, Decodable { 47 | case rectangle = "Rectangle" 48 | case ellipse = "Ellipse" 49 | case text = "Text" 50 | case image = "Image" 51 | case barcode = "Barcode" 52 | } 53 | 54 | 55 | required init(from decoder: Decoder) throws { 56 | let container = try decoder.container(keyedBy: CodingKeys.self) 57 | var shapesArrayForType = try container.nestedUnkeyedContainer(forKey: CodingKeys.shapelist) 58 | var newShapeList = [ShapeX]() 59 | var shapesArray = shapesArrayForType 60 | 61 | while(!shapesArrayForType.isAtEnd) 62 | { 63 | 64 | let shape = try shapesArrayForType.nestedContainer(keyedBy: ShapeTypeKey.self) //causes the 65 | let type = try shape.decode(ShapeTypes.self, forKey: ShapeTypeKey.type) 66 | 67 | switch type { 68 | case .rectangle: 69 | print("found rectangle") 70 | newShapeList.append(try shapesArray.decode(RectangleX.self)) //since currentIndex of shapesArray is still at previous location, decode the object 71 | case .ellipse: 72 | print("found ellipse") 73 | newShapeList.append(try shapesArray.decode(EllipseX.self)) 74 | case .image: 75 | print("found image") 76 | newShapeList.append(try shapesArray.decode(ImageX.self)) 77 | case .text: 78 | print("found text") 79 | newShapeList.append(try shapesArray.decode(TextX.self)) 80 | case .barcode: 81 | print("found barcode") 82 | newShapeList.append(try shapesArray.decode(BarcodeX.self)) 83 | } 84 | } 85 | self.shapeList = newShapeList 86 | } 87 | 88 | func encode(to encoder: Encoder) throws { 89 | var container = encoder.container(keyedBy: CodingKeys.self) 90 | try container.encode(shapeList, forKey: .shapelist) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Shapes/TextX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | class TextX: ShapeX { 14 | 15 | @Published var fontSize:CGFloat = 16.0*300.0/72.0 16 | @Published var text:String = "Text" 17 | @Published var originalText:String = "Text" 18 | @Published var fontName:String = "Arial" 19 | @Published var textColor = Color.black 20 | @Published var horizontalTextAlignment:String = "Center" 21 | 22 | init(_ dpi:Double, _ location: CGPoint, _ size: CGSize, _ canvasSize: CGSize, _ isSelected: Bool) { 23 | 24 | super.init(dpi,"Text",location,size,canvasSize,isSelected) 25 | fontSize=16.0*dpi/72.0 26 | 27 | } 28 | 29 | enum CodingKeys : String, CodingKey { 30 | case fontSize 31 | case text 32 | //case originalText 33 | case fontName 34 | case textColor 35 | case horizontalTextAlignment 36 | } 37 | 38 | required init(from decoder: Decoder) throws { 39 | try super.init(from: decoder) 40 | let values = try decoder.container(keyedBy: CodingKeys.self) 41 | fontSize = try values.decodeIfPresent(CGFloat.self, forKey: .fontSize) ?? 18.0*dpi/72.0 42 | text = try values.decodeIfPresent(String.self, forKey: .text) ?? "Text" 43 | fontName = try values.decodeIfPresent(String.self, forKey: .fontName) ?? "Arial" 44 | 45 | let textColorData = try values.decodeIfPresent(Data.self, forKey: .textColor) ?? nil 46 | if textColorData != nil 47 | { 48 | let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: textColorData!) 49 | textColor = Color(color!) 50 | } 51 | horizontalTextAlignment = try values.decodeIfPresent(String.self, forKey: .horizontalTextAlignment) ?? "Center" 52 | 53 | } 54 | 55 | override func encode(to encoder: Encoder) throws { 56 | try super.encode(to: encoder) 57 | var container = encoder.container(keyedBy: CodingKeys.self) 58 | try container.encode(fontSize, forKey: .fontSize) 59 | try container.encode(text, forKey: .text) 60 | try container.encode(fontName, forKey: .fontName) 61 | let convertedTextColor = UIColor(textColor) 62 | let textColorData = try NSKeyedArchiver.archivedData(withRootObject: convertedTextColor, requiringSecureCoding: false) 63 | try container.encode(textColorData, forKey: .textColor) 64 | try container.encode(horizontalTextAlignment, forKey: .horizontalTextAlignment) 65 | 66 | } 67 | 68 | override func view() -> AnyView { 69 | AnyView( 70 | HStack{ 71 | if horizontalTextAlignment == "Right" { Spacer() } 72 | Text(self.text) 73 | .lineLimit(1) 74 | .font(.custom(self.fontName, size: self.fontSize)) 75 | .foregroundColor(self.textColor) 76 | .fixedSize(horizontal: true, vertical: false) 77 | if horizontalTextAlignment == "Left" { Spacer() } 78 | } 79 | .frame(width: self.size.width, height: self.size.height) 80 | .clipShape(Rectangle()) 81 | .position(self.location) 82 | ) 83 | } 84 | 85 | override func useElipsisIfSizeTooSmall() { 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Util/LabelAction.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import Foundation 12 | class LabelAction 13 | { 14 | let pageSettings: PageSettings 15 | let shapes: ShapesX 16 | 17 | init(shapes: ShapesX, pageSettings: PageSettings) { 18 | self.shapes = shapes 19 | self.pageSettings = pageSettings 20 | } 21 | 22 | func generate() -> String{ 23 | let encoder=JSONEncoder() 24 | encoder.outputFormatting = .prettyPrinted 25 | 26 | let shapesData = (try? encoder.encode(shapes))! 27 | let shapesDataStr = String(data: shapesData, encoding: .utf8)! 28 | let pageSettingsData = (try? encoder.encode(pageSettings))! 29 | let pageSettingsDataStr = String(data: pageSettingsData, encoding: .utf8)! 30 | 31 | var jsonObject: [String: String] = [String: String]() 32 | 33 | jsonObject["page_settings"]=pageSettingsDataStr 34 | jsonObject["shapes"]=shapesDataStr 35 | 36 | if let jsonData = try? encoder.encode(jsonObject) { 37 | if let jsonString = String(data: jsonData, encoding: .utf8) { 38 | //print(jsonString) 39 | 40 | var jsonLabel: [String: String] = [String: String]() 41 | jsonLabel["label"]=jsonString 42 | 43 | if let jsonLabelData = try? encoder.encode(jsonLabel) { 44 | if let jsonLabelString = String(data: jsonLabelData, encoding: .utf8) { 45 | 46 | return jsonLabelString 47 | 48 | } 49 | } 50 | 51 | } 52 | } 53 | 54 | return "" 55 | } 56 | 57 | func load(jsonLabelStr: String) 58 | { 59 | if let data = jsonLabelStr.data(using: .utf8) { 60 | //do { 61 | let labelDictionary : [String: Any] = (try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any])! 62 | let labelStr = labelDictionary["label"] as? String 63 | 64 | if let attributesData = labelStr!.data(using: .utf8) { 65 | let attributesDictionary : [String: Any] = (try? JSONSerialization.jsonObject(with: attributesData, options: []) as? [String: Any])! 66 | let loadedPageSettingsStr = attributesDictionary["page_settings"] as? String 67 | let loadedPageSettings = (try? JSONDecoder().decode(PageSettings.self, from: loadedPageSettingsStr!.data(using: .utf8)!))! 68 | let loadedShapesStr = attributesDictionary["shapes"] as? String 69 | let loadedShapes = (try? JSONDecoder().decode(ShapesX.self, from: loadedShapesStr!.data(using: .utf8)!))! 70 | pageSettings.dpi=loadedPageSettings.dpi 71 | pageSettings.name=loadedPageSettings.name 72 | pageSettings.category=loadedPageSettings.category 73 | pageSettings.vendor=loadedPageSettings.vendor 74 | pageSettings.description=loadedPageSettings.description 75 | pageSettings.type=loadedPageSettings.type 76 | pageSettings.pageWidth=loadedPageSettings.pageWidth 77 | pageSettings.pageHeight=loadedPageSettings.pageHeight 78 | pageSettings.labelWidth=loadedPageSettings.labelWidth 79 | pageSettings.labelHeight=loadedPageSettings.labelHeight 80 | pageSettings.leftMargin=loadedPageSettings.leftMargin 81 | pageSettings.topMargin=loadedPageSettings.topMargin 82 | pageSettings.labelWidth=loadedPageSettings.labelWidth 83 | pageSettings.hSpace=loadedPageSettings.hSpace 84 | pageSettings.vSpace=loadedPageSettings.vSpace 85 | pageSettings.numRows=loadedPageSettings.numRows 86 | pageSettings.numCols=loadedPageSettings.numCols 87 | pageSettings.dpi=loadedPageSettings.dpi 88 | 89 | shapes.shapeList=loadedShapes.shapeList 90 | pageSettings.generateLabels() //File Open 91 | 92 | print("loadedPageSettings:",loadedPageSettings.name,":",loadedPageSettings.numRows,":",loadedPageSettings.numCols) 93 | 94 | } 95 | 96 | //} catch { 97 | // print(error.localizedDescription) 98 | //} 99 | } 100 | 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/BarcodePropertiesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class BarcodePropertiesViewModel: ObjectBaseViewModel { 10 | 11 | @Published var input: String = "1234" 12 | @Published var symbology: String = "Code 39" 13 | @Published var checkDigit: Bool = false 14 | @Published var barcode: String = "*1234*" 15 | 16 | @Published var humanReadableText: String = "*1234*" 17 | 18 | @Published var fontName: String = "CCode39_S3" 19 | @Published var fontSize: Double = 18.0 20 | @Published var textColor = Color.black 21 | 22 | @Published var hrFontName: String = "Arial" 23 | @Published var hrFontSize: Double = 16.0 24 | @Published var hrTextColor = Color.black 25 | 26 | @Published var alignment: String = "Center" 27 | 28 | var skipOnChange = false 29 | func setupSelectedProperties() 30 | { 31 | 32 | self.shapes.shapeList.forEach 33 | { 34 | if $0.isSelected == true { 35 | let selectedText = $0 as! BarcodeX 36 | 37 | self.input = selectedText.input 38 | 39 | self.checkDigit = selectedText.checkDigit 40 | print(selectedText.checkDigit) 41 | 42 | self.symbology = selectedText.symbology 43 | //bad fix 44 | if self.symbology == "Code 39" 45 | { 46 | skipOnChange=false 47 | } 48 | else 49 | { 50 | skipOnChange=true 51 | } 52 | self.barcode = selectedText.barcode 53 | self.humanReadableText = selectedText.humanReadableText 54 | 55 | self.textColor = selectedText.textColor 56 | self.fontName = selectedText.fontName 57 | print(fontName) 58 | self.fontSize = selectedText.fontSize 59 | 60 | self.hrTextColor = selectedText.hrTextColor 61 | self.hrFontName = selectedText.hrFontName 62 | self.hrFontSize = selectedText.hrFontSize 63 | 64 | self.alignment = selectedText.horizontalTextAlignment 65 | //self.zIndex = String(format: "%.0f", selectedText.zIndex) 66 | let tX = (selectedText.location.x - CGFloat(selectedText.size.width)/2.0) * 72.0 / 300.0 67 | let tY = (selectedText.location.y - CGFloat(selectedText.size.height)/2.0) * 72.0 / 300.0 68 | let tWidth = selectedText.size.width * 72.0 / 300.0 69 | let tHeight = selectedText.size.height * 72.0 / 300.0 70 | let tzIndex = selectedText.zIndex 71 | super.setupSelectedProperties(x:tX,y:tY,width:tWidth,height:tHeight,zIndex:tzIndex) 72 | 73 | } 74 | } 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/BaseViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class BaseViewModel: ObservableObject { 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/FileViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class FilesViewModel: BaseViewModel { 10 | 11 | @Published var files: [String] = [] 12 | @Published var selectedFileName: String = "" 13 | 14 | func setupSelectedProperties() 15 | { 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/ImagePropertiesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class ImagePropertiesViewModel: ObjectBaseViewModel { 10 | 11 | func setupSelectedProperties() 12 | { 13 | self.shapes.shapeList.forEach 14 | { 15 | if $0.isSelected == true { 16 | if $0 is ImageX 17 | { 18 | let selectedShape = $0 as! ImageX 19 | 20 | let tX = (selectedShape.location.x - CGFloat(selectedShape.size.width)/2.0) * 72.0 / 300.0 21 | let tY = (selectedShape.location.y - CGFloat(selectedShape.size.height)/2.0) * 72.0 / 300.0 22 | let tWidth = selectedShape.size.width * 72.0 / 300.0 23 | let tHeight = selectedShape.size.height * 72.0 / 300.0 24 | let tzIndex = selectedShape.zIndex 25 | super.setupSelectedProperties(x:tX,y:tY,width:tWidth,height:tHeight,zIndex:tzIndex) 26 | 27 | } 28 | 29 | } 30 | } 31 | } 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/ObjectBaseViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | class ObjectBaseViewModel: ObservableObject { 11 | 12 | @Published var x: String = "" 13 | @Published var y: String = "" 14 | @Published var width: String = "" 15 | @Published var height: String = "" 16 | @Published var zIndex: String = "0" 17 | 18 | let shapes: ShapesX 19 | init(shapes: ShapesX) { 20 | self.shapes = shapes 21 | } 22 | 23 | func setupSelectedProperties(x: CGFloat, y:CGFloat, width:CGFloat, height:CGFloat, zIndex: Double) 24 | { 25 | print("setup x:",x) 26 | self.x = String(format: "%.0f", x) 27 | self.y = String(format: "%.0f", y) 28 | self.width = String(format: "%.0f", width) 29 | self.height = String(format: "%.0f", height) 30 | self.zIndex = String(format: "%.0f", zIndex) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/PreviewViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class PreviewViewModel: BaseViewModel { 10 | 11 | @Published var startLabel: String = "1" 12 | @Published var numPages: String = "1" 13 | @Published var orientation: String = "Landscape" 14 | 15 | @Published var istartLabel : Int = 1 16 | @Published var inumPages : Int = 1 17 | @Published var scaleFactor : Double = 0.11 18 | @Published var printBorder : Bool = true 19 | 20 | init(dpi : Double, pageType: String) 21 | { 22 | super.init() 23 | let dpiFactor = 300.0/dpi 24 | scaleFactor = pageType == "envelope-landscape" ? 0.05 : 0.11 25 | scaleFactor = scaleFactor*dpiFactor 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/ShapePropertiesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class ShapePropertiesViewModel: ObjectBaseViewModel { 10 | 11 | @Published var strokeWidth: Double = 1.0 12 | @Published var strokeColor = Color.black 13 | @Published var fillColor = Color.white 14 | func setupSelectedProperties() 15 | { 16 | self.shapes.shapeList.forEach 17 | { 18 | if $0.isSelected == true { 19 | if $0 is RectangleX 20 | { 21 | let selectedShape = $0 as! RectangleX 22 | print(selectedShape.strokeWidth) 23 | self.strokeWidth = selectedShape.strokeWidth 24 | self.strokeColor = selectedShape.strokeColor 25 | self.fillColor = selectedShape.fillColor 26 | //self.zIndex = String(format: "%.0f", selectedShape.zIndex) 27 | 28 | let tX = (selectedShape.location.x - CGFloat(selectedShape.size.width)/2.0) * 72.0 / 300.0 29 | let tY = (selectedShape.location.y - CGFloat(selectedShape.size.height)/2.0) * 72.0 / 300.0 30 | let tWidth = selectedShape.size.width * 72.0 / 300.0 31 | let tHeight = selectedShape.size.height * 72.0 / 300.0 32 | let tzIndex = selectedShape.zIndex 33 | super.setupSelectedProperties(x:tX,y:tY,width:tWidth,height:tHeight,zIndex:tzIndex) 34 | 35 | } 36 | else if $0 is EllipseX 37 | { 38 | let selectedShape = $0 as! EllipseX 39 | self.strokeWidth = selectedShape.strokeWidth 40 | self.strokeColor = selectedShape.strokeColor 41 | self.fillColor = selectedShape.fillColor 42 | //self.zIndex = String(format: "%.0f", selectedShape.zIndex) 43 | 44 | let tX = (selectedShape.location.x - CGFloat(selectedShape.size.width)/2.0) * 72.0 / 300.0 45 | let tY = (selectedShape.location.y - CGFloat(selectedShape.size.height)/2.0) * 72.0 / 300.0 46 | let tWidth = selectedShape.size.width * 72.0 / 300.0 47 | let tHeight = selectedShape.size.height * 72.0 / 300.0 48 | let tzIndex = selectedShape.zIndex 49 | super.setupSelectedProperties(x:tX,y:tY,width:tWidth,height:tHeight,zIndex:tzIndex) 50 | 51 | } 52 | 53 | } 54 | } 55 | } 56 | 57 | 58 | 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/TemplateViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class TemplateViewModel: BaseViewModel { 10 | @Published var name: String = "Standard SLE005" 11 | @Published var category: String = "Labels" 12 | @Published var vendor: String = "Standard" 13 | @Published var description: String = "Address Label (Letter) - 10x3" 14 | @Published var type: String = "iso-letter" 15 | @Published var units: String = "Inches" 16 | @Published var pageWidth: String = "8.5" 17 | @Published var pageHeight: String = "11.0" 18 | @Published var leftMargin: String = "0.188" 19 | @Published var topMargin: String = "0.5" 20 | @Published var labelWidth: String = "2.625" 21 | @Published var labelHeight: String = "1.0" 22 | @Published var hSpace: String = "0.125" 23 | @Published var vSpace: String = "0.0" 24 | @Published var numRows: String = "10" 25 | @Published var numCols: String = "3" 26 | @Published var selectedTemplateIndex: Int = 0 27 | @Published var templateCount: Int = 11 28 | 29 | @Published var previewFactor: Double = 0.25 30 | 31 | let pageSettings: PageSettings 32 | init(units: String, pageSettings: PageSettings) { 33 | self.units=units 34 | self.pageSettings = pageSettings 35 | 36 | name = pageSettings.name 37 | category = pageSettings.category 38 | print("PS:",pageSettings.vendor) 39 | vendor = pageSettings.vendor 40 | description = pageSettings.description 41 | type = pageSettings.type 42 | 43 | var inchOrCM = 1.0 44 | if units != "Inches" 45 | { 46 | inchOrCM = 2.54 47 | } 48 | pageWidth = String(format: "%.3f", pageSettings.pageWidth*inchOrCM) 49 | pageHeight = String(format: "%.3f", pageSettings.pageHeight*inchOrCM) 50 | leftMargin = String(format: "%.3f", pageSettings.leftMargin*inchOrCM) 51 | topMargin = String(format: "%.3f", pageSettings.topMargin*inchOrCM) 52 | labelWidth = String(format: "%.3f", pageSettings.labelWidth*inchOrCM) 53 | labelHeight = String(format: "%.3f", pageSettings.labelHeight*inchOrCM) 54 | hSpace = String(format: "%.3f", pageSettings.hSpace*inchOrCM) 55 | vSpace = String(format: "%.3f", pageSettings.vSpace*inchOrCM) 56 | numRows = String(pageSettings.numRows) 57 | numCols = String(pageSettings.numCols) 58 | 59 | } 60 | 61 | func setupPageSettingsAndTemplateModel(tempPageSettings: PageSettings, selectedTemplateIndex: Int) 62 | { 63 | 64 | let x = selectedTemplateIndex 65 | print("setupPageSettings:",x) 66 | tempPageSettings.name = labelTemplates[x][0] 67 | tempPageSettings.category = labelTemplates[x][1] 68 | tempPageSettings.vendor = labelTemplates[x][2] 69 | tempPageSettings.description = labelTemplates[x][3] 70 | tempPageSettings.type = labelTemplates[x][4] 71 | print(tempPageSettings.description) 72 | if tempPageSettings.type == "envelope-landscape" 73 | { 74 | tempPageSettings.pageHeight = Double(labelTemplates[x][5]) ?? 1.0 75 | tempPageSettings.pageWidth = Double(labelTemplates[x][6]) ?? 1.0 76 | tempPageSettings.labelHeight = Double(labelTemplates[x][7]) ?? 1.0 77 | tempPageSettings.labelWidth = Double(labelTemplates[x][8]) ?? 1.0 78 | tempPageSettings.vSpace = Double(labelTemplates[x][9]) ?? 0.0 79 | tempPageSettings.hSpace = Double(labelTemplates[x][10]) ?? 0.0 80 | tempPageSettings.numCols = Int(labelTemplates[x][11]) ?? 1 81 | tempPageSettings.numRows = Int(labelTemplates[x][12]) ?? 1 82 | tempPageSettings.topMargin = Double(labelTemplates[x][13]) ?? 0.0 83 | tempPageSettings.leftMargin = Double(labelTemplates[x][14]) ?? 0.0 84 | } 85 | else{ 86 | tempPageSettings.pageWidth = Double(labelTemplates[x][5]) ?? 1.0 87 | tempPageSettings.pageHeight = Double(labelTemplates[x][6]) ?? 1.0 88 | tempPageSettings.labelWidth = Double(labelTemplates[x][7]) ?? 1.0 89 | tempPageSettings.labelHeight = Double(labelTemplates[x][8]) ?? 1.0 90 | tempPageSettings.hSpace = Double(labelTemplates[x][9]) ?? 0.0 91 | tempPageSettings.vSpace = Double(labelTemplates[x][10]) ?? 0.0 92 | tempPageSettings.numRows = Int(labelTemplates[x][11]) ?? 1 93 | tempPageSettings.numCols = Int(labelTemplates[x][12]) ?? 1 94 | tempPageSettings.leftMargin = Double(labelTemplates[x][13]) ?? 0.0 95 | tempPageSettings.topMargin = Double(labelTemplates[x][14]) ?? 0.0 96 | } 97 | 98 | tempPageSettings.generateLabels(dpi:72) 99 | 100 | 101 | name = tempPageSettings.name 102 | category = tempPageSettings.category 103 | vendor = tempPageSettings.vendor 104 | description = tempPageSettings.description 105 | type = tempPageSettings.type 106 | var inchOrCM = 1.0 107 | if units != "Inches" 108 | { 109 | inchOrCM = 2.54 110 | } 111 | pageWidth = String(format: "%.3f", tempPageSettings.pageWidth*inchOrCM) 112 | pageHeight = String(format: "%.3f", tempPageSettings.pageHeight*inchOrCM) 113 | leftMargin = String(format: "%.3f", tempPageSettings.leftMargin*inchOrCM) 114 | topMargin = String(format: "%.3f", tempPageSettings.topMargin*inchOrCM) 115 | labelWidth = String(format: "%.3f", tempPageSettings.labelWidth*inchOrCM) 116 | labelHeight = String(format: "%.3f", tempPageSettings.labelHeight*inchOrCM) 117 | hSpace = String(format: "%.3f", tempPageSettings.hSpace*inchOrCM) 118 | vSpace = String(format: "%.3f", tempPageSettings.vSpace*inchOrCM) 119 | numRows = String(tempPageSettings.numRows) 120 | numCols = String(tempPageSettings.numCols) 121 | } 122 | 123 | 124 | //use by create label only 125 | func setupPageSettings(selectedTemplateIndex: Int) 126 | { 127 | let x = selectedTemplateIndex 128 | print("setupPageSettings:",x) 129 | pageSettings.name = labelTemplates[x][0] 130 | pageSettings.category = labelTemplates[x][1] 131 | pageSettings.vendor = labelTemplates[x][2] 132 | pageSettings.description = labelTemplates[x][3] 133 | pageSettings.type = labelTemplates[x][4] 134 | if pageSettings.type == "envelope-landscape" 135 | { 136 | pageSettings.pageHeight = Double(labelTemplates[x][5]) ?? 1.0 137 | pageSettings.pageWidth = Double(labelTemplates[x][6]) ?? 1.0 138 | pageSettings.labelHeight = Double(labelTemplates[x][7]) ?? 1.0 139 | pageSettings.labelWidth = Double(labelTemplates[x][8]) ?? 1.0 140 | pageSettings.vSpace = Double(labelTemplates[x][9]) ?? 0.0 141 | pageSettings.hSpace = Double(labelTemplates[x][10]) ?? 0.0 142 | pageSettings.numCols = Int(labelTemplates[x][11]) ?? 1 143 | pageSettings.numRows = Int(labelTemplates[x][12]) ?? 1 144 | pageSettings.topMargin = Double(labelTemplates[x][13]) ?? 0.0 145 | pageSettings.leftMargin = Double(labelTemplates[x][14]) ?? 0.0 146 | } 147 | else{ 148 | pageSettings.pageWidth = Double(labelTemplates[x][5]) ?? 1.0 149 | pageSettings.pageHeight = Double(labelTemplates[x][6]) ?? 1.0 150 | pageSettings.labelWidth = Double(labelTemplates[x][7]) ?? 1.0 151 | pageSettings.labelHeight = Double(labelTemplates[x][8]) ?? 1.0 152 | pageSettings.hSpace = Double(labelTemplates[x][9]) ?? 0.0 153 | pageSettings.vSpace = Double(labelTemplates[x][10]) ?? 0.0 154 | pageSettings.numRows = Int(labelTemplates[x][11]) ?? 1 155 | pageSettings.numCols = Int(labelTemplates[x][12]) ?? 1 156 | pageSettings.leftMargin = Double(labelTemplates[x][13]) ?? 0.0 157 | pageSettings.topMargin = Double(labelTemplates[x][14]) ?? 0.0 158 | } 159 | 160 | pageSettings.generateLabels() 161 | } 162 | 163 | } 164 | 165 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/ViewModels/TextPropertiesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelMainViewModel.swift 3 | // 4 | // 5 | // Created by on 23/3/22. 6 | // 7 | import SwiftUI 8 | 9 | class TextPropertiesViewModel: ObjectBaseViewModel { 10 | 11 | @Published var text: String = "Text" 12 | @Published var fontName: String = "Arial" 13 | @Published var fontSize: Double = 18.0 14 | @Published var textColor = Color.black 15 | @Published var alignment: String = "Center" 16 | //@Published var zIndex: String = "0" 17 | 18 | 19 | func setupSelectedProperties() 20 | { 21 | self.shapes.shapeList.forEach 22 | { 23 | if $0.isSelected == true { 24 | let selectedText = $0 as! TextX 25 | 26 | self.text = selectedText.text 27 | self.textColor = selectedText.textColor 28 | self.fontName = selectedText.fontName 29 | self.fontSize = selectedText.fontSize 30 | self.alignment = selectedText.horizontalTextAlignment 31 | //self.zIndex = String(format: "%.0f", selectedText.zIndex) 32 | 33 | 34 | 35 | /* 36 | self.x = String(format: "%.0f", (selectedText.location.x - CGFloat(selectedText.size.width)/2.0) * 72.0 / 300.0) 37 | self.y = String(format: "%.0f", (selectedText.location.y - CGFloat(selectedText.size.height)/2.0) * 72.0 / 300.0) 38 | self.width = String(format: "%.0f", selectedText.size.width * 72.0 / 300.0) 39 | self.height = String(format: "%.0f", selectedText.size.height * 72.0 / 300.0) 40 | */ 41 | let tX = (selectedText.location.x - CGFloat(selectedText.size.width)/2.0) * 72.0 / 300.0 42 | let tY = (selectedText.location.y - CGFloat(selectedText.size.height)/2.0) * 72.0 / 300.0 43 | let tWidth = selectedText.size.width * 72.0 / 300.0 44 | let tHeight = selectedText.size.height * 72.0 / 300.0 45 | let tzIndex = selectedText.zIndex 46 | super.setupSelectedProperties(x:tX,y:tY,width:tWidth,height:tHeight,zIndex:tzIndex) 47 | 48 | } 49 | } 50 | } 51 | 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/AlertWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertWrapper.swift 3 | // 4 | // 5 | // Created by on 7/4/22. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | //https://stackoverflow.com/questions/56726663/how-to-add-a-textfield-to-alert-in-swiftui 11 | class TextFieldAlertViewController: UIViewController { 12 | 13 | /// Presents a UIAlertController (alert style) with a UITextField and a `Done` button 14 | /// - Parameters: 15 | /// - title: to be used as title of the UIAlertController 16 | /// - message: to be used as optional message of the UIAlertController 17 | /// - text: binding for the text typed into the UITextField 18 | /// - isPresented: binding to be set to false when the alert is dismissed (`Done` button tapped) 19 | init(title: String, message: String?, labelContent: String, text: Binding, isPresented: Binding?, saveSuccess: Binding?, existingLabelExist: Binding?, alertMessage: Binding) { 20 | self.alertTitle = title 21 | self.message = message 22 | self.labelContent = labelContent 23 | self._text = text 24 | self.isPresented = isPresented 25 | self.saveSuccess = saveSuccess 26 | self.existingLabelExist = existingLabelExist 27 | self._alertMessage = alertMessage 28 | 29 | super.init(nibName: nil, bundle: nil) 30 | } 31 | 32 | required init?(coder: NSCoder) { 33 | fatalError("init(coder:) has not been implemented") 34 | } 35 | 36 | // MARK: - Dependencies 37 | private let alertTitle: String 38 | private let message: String? 39 | private let labelContent: String 40 | @Binding private var text: String? 41 | private var isPresented: Binding? 42 | private var saveSuccess: Binding? 43 | private var existingLabelExist: Binding? 44 | @Binding private var alertMessage: String? 45 | 46 | // MARK: - Private Properties 47 | private var subscription: AnyCancellable? 48 | 49 | // MARK: - Lifecycle 50 | override func viewDidAppear(_ animated: Bool) { 51 | super.viewDidAppear(animated) 52 | presentAlertController() 53 | } 54 | 55 | private func presentAlertController() { 56 | guard subscription == nil else { return } // present only once 57 | 58 | let vc = UIAlertController(title: alertTitle, message: message, preferredStyle: .alert) 59 | 60 | // add a textField and create a subscription to update the `text` binding 61 | vc.addTextField { [weak self] textField in 62 | guard let self = self else { return } 63 | self.subscription = NotificationCenter.default 64 | .publisher(for: UITextField.textDidChangeNotification, object: textField) 65 | .map { ($0.object as? UITextField)?.text } 66 | .assign(to: \.text, on: self) 67 | } 68 | 69 | let caction = UIAlertAction(title: "Cancel", style: .default) { [weak self] _ in 70 | self?.isPresented?.wrappedValue = false 71 | self?.saveSuccess?.wrappedValue = false 72 | 73 | } 74 | 75 | // create a `Done` action that updates the `isPresented` binding when tapped 76 | // this is just for Demo only but we should really inject 77 | // an array of buttons (with their title, style and tap handler) 78 | let action = UIAlertAction(title: "Save", style: .default) { [weak self] _ in 79 | self?.isPresented?.wrappedValue = false 80 | 81 | 82 | if self!.text != nil 83 | { 84 | 85 | } 86 | else 87 | { 88 | self?.saveSuccess?.wrappedValue = true //actually not success but reuse 89 | self?.alertMessage = Optional.some("Please enter a valid label name for saving.") 90 | return 91 | } 92 | 93 | if self!.text! == "" 94 | { 95 | self?.saveSuccess?.wrappedValue = true //actually not success but reuse 96 | self?.alertMessage = Optional.some("Please enter a valid label name for saving.") 97 | return 98 | } 99 | 100 | print("Saving:",self!.text!) 101 | 102 | let file = self!.text! 103 | 104 | if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { 105 | 106 | let fileURL = dir.appendingPathComponent(file) 107 | let fileManager = FileManager.default 108 | print(fileURL.path) 109 | print(file) 110 | 111 | if fileManager.fileExists(atPath: fileURL.path) { 112 | print("FILE AVAILABLE") 113 | self?.saveSuccess?.wrappedValue = true 114 | self?.alertMessage = Optional.some("An existing label with the same name already exist. Please enter another label name and try saving again.") 115 | } else { 116 | print("FILE NOT AVAILABLE") 117 | do { 118 | try self!.labelContent.write(to: fileURL, atomically: false, encoding: .utf8) 119 | self?.saveSuccess?.wrappedValue = true 120 | self?.alertMessage = Optional.some("Label saved successfully.") 121 | } 122 | catch { 123 | self?.text = Optional.some("") 124 | } 125 | } 126 | self?.text = Optional.some("") 127 | 128 | } 129 | 130 | 131 | } 132 | 133 | vc.addAction(caction) 134 | vc.addAction(action) 135 | present(vc, animated: true, completion: nil) 136 | } 137 | } 138 | 139 | struct TextFieldAlert { 140 | 141 | // MARK: Properties 142 | let title: String 143 | let message: String? 144 | let labelContent: String 145 | @Binding var text: String? 146 | var isPresented: Binding? = nil 147 | var saveSuccess: Binding? = nil 148 | var existingLabelExist: Binding? = nil 149 | @Binding var alertMessage: String? 150 | 151 | // MARK: Modifiers 152 | func dismissable(_ isPresented: Binding) -> TextFieldAlert { 153 | TextFieldAlert(title: title, message: message,labelContent:labelContent, text: $text, isPresented: isPresented, saveSuccess: saveSuccess, existingLabelExist: existingLabelExist,alertMessage: $alertMessage) 154 | } 155 | } 156 | 157 | extension TextFieldAlert: UIViewControllerRepresentable { 158 | 159 | typealias UIViewControllerType = TextFieldAlertViewController 160 | 161 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIViewControllerType { 162 | TextFieldAlertViewController(title: title, message: message, labelContent: labelContent, text: $text, isPresented: isPresented, saveSuccess: saveSuccess, existingLabelExist: existingLabelExist, alertMessage: $alertMessage) 163 | } 164 | 165 | func updateUIViewController(_ uiViewController: UIViewControllerType, 166 | context: UIViewControllerRepresentableContext) { 167 | // no update needed 168 | } 169 | } 170 | 171 | struct TextFieldWrapper: View { 172 | 173 | @Binding var isPresented: Bool 174 | let presentingView: PresentingView 175 | let content: () -> TextFieldAlert 176 | 177 | var body: some View { 178 | ZStack { 179 | if (isPresented) { content().dismissable($isPresented) } 180 | presentingView 181 | } 182 | } 183 | } 184 | 185 | extension View { 186 | func textFieldAlert(isPresented: Binding, 187 | content: @escaping () -> TextFieldAlert) -> some View { 188 | TextFieldWrapper(isPresented: isPresented, 189 | presentingView: self, 190 | content: content) 191 | } 192 | } 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/DimensionsPropertiesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DimensionPropertiesViewX.swift 3 | // 4 | // 5 | // Created by on 21/4/22. 6 | // 7 | import SwiftUI 8 | 9 | struct DimensionsPropertiesViewX: View { 10 | 11 | 12 | @EnvironmentObject var shapes: ShapesX 13 | @StateObject var objectPropertiesViewModel: ObjectBaseViewModel 14 | 15 | var body: some View { 16 | 17 | Section(header: Text("Dimensions (Pixels)")){ 18 | 19 | HStack{ 20 | Text("X") 21 | Spacer() 22 | 23 | TextField("X", text: $objectPropertiesViewModel.x) 24 | .multilineTextAlignment(.trailing) 25 | .frame(width: 100) 26 | .fixedSize() 27 | .onChange(of: objectPropertiesViewModel.x) { newValue in 28 | self.shapes.shapeList.forEach 29 | { 30 | let trimStr = newValue.count > 4 ? String(newValue.prefix(4)) : newValue 31 | if $0.isSelected == true { 32 | let selectedText = $0 //as! TextX 33 | if let n = NumberFormatter().number(from: trimStr) { 34 | 35 | //number is assumed to be in inch x 72pixels 36 | let xIn72 = CGFloat(truncating: n) 37 | let widthIn300 = CGFloat($0.size.width)/2.0 38 | let finalX = xIn72*300.0/72.0 + widthIn300 39 | //print("imvs:",selectedText.imageViewSize.width) 40 | //print("sts:",selectedText.size.width) 41 | //print("fx:",finalX) 42 | 43 | if finalX >= 0.0 && 44 | finalX <= selectedText.canvasSize.width 45 | { 46 | selectedText.location.x = finalX 47 | } 48 | 49 | //set to max so that behavior seems ok 50 | if finalX > selectedText.canvasSize.width 51 | { 52 | selectedText.location.x = selectedText.canvasSize.width 53 | } 54 | 55 | print(selectedText.location.x) 56 | } 57 | } 58 | } 59 | } 60 | }//HStack 61 | HStack{ 62 | Text("Y") 63 | Spacer() 64 | TextField("Y", text: $objectPropertiesViewModel.y) 65 | .multilineTextAlignment(.trailing) 66 | .frame(width: 100) 67 | .fixedSize() 68 | .onChange(of: objectPropertiesViewModel.y) { newValue in 69 | self.shapes.shapeList.forEach 70 | { 71 | let trimStr = newValue.count > 4 ? String(newValue.prefix(4)) : newValue 72 | if $0.isSelected == true { 73 | let selectedText = $0 //as! TextX 74 | if let n = NumberFormatter().number(from: trimStr) { 75 | 76 | //number is assumed to be in inch x 72pixels 77 | let yIn72 = CGFloat(truncating: n) 78 | let heightIn300 = CGFloat($0.size.height)/2.0 79 | let finalY = yIn72*300.0/72.0 + heightIn300 80 | 81 | if finalY >= 0.0 && 82 | finalY <= selectedText.canvasSize.height 83 | { 84 | selectedText.location.y = finalY 85 | } 86 | 87 | //set to max so that behavior seems ok 88 | if finalY > selectedText.canvasSize.height 89 | { 90 | selectedText.location.y = selectedText.canvasSize.height 91 | } 92 | print(selectedText.location.y) 93 | } 94 | } 95 | } 96 | } 97 | }//HStack 98 | 99 | HStack{ 100 | Text("Width") 101 | Spacer() 102 | TextField("Width", text: $objectPropertiesViewModel.width) 103 | .multilineTextAlignment(.trailing) 104 | .frame(width: 100) 105 | .fixedSize() 106 | .onChange(of: objectPropertiesViewModel.width) { newValue in 107 | self.shapes.shapeList.forEach 108 | { 109 | let trimStr = newValue.count > 4 ? String(newValue.prefix(4)) : newValue 110 | if $0.isSelected == true { 111 | let selectedText = $0 //as! TextX 112 | if let n = NumberFormatter().number(from: trimStr) { 113 | 114 | //number is assumed to be in inch x 72pixels 115 | let widthIn72 = CGFloat(truncating: n) 116 | let finalWidth = widthIn72*300.0/72.0 117 | 118 | if finalWidth > 0.0 && 119 | finalWidth < selectedText.canvasSize.width 120 | { 121 | selectedText.size.width = finalWidth 122 | } 123 | 124 | if finalWidth < 80.0 125 | { 126 | selectedText.size.width = 80.0 127 | } 128 | 129 | //set to max so that behavior seems ok 130 | if finalWidth > selectedText.canvasSize.width - 20 131 | { 132 | selectedText.size.width = selectedText.canvasSize.width - 20 133 | } 134 | 135 | //update x as width changes in both directions 136 | objectPropertiesViewModel.x = String(format: "%.0f", (selectedText.location.x - CGFloat(selectedText.size.width)/2.0) * 72.0 / 300.0) 137 | print(selectedText.size.width) 138 | } 139 | } 140 | } 141 | } 142 | }//HStack 143 | 144 | HStack{ 145 | Text("Height") 146 | Spacer() 147 | TextField("Height", text: $objectPropertiesViewModel.height) 148 | .multilineTextAlignment(.trailing) 149 | .frame(width: 100) 150 | .fixedSize() 151 | .onChange(of: objectPropertiesViewModel.height) { newValue in 152 | self.shapes.shapeList.forEach 153 | { 154 | let trimStr = newValue.count > 4 ? String(newValue.prefix(4)) : newValue 155 | if $0.isSelected == true { 156 | let selectedText = $0 //as! TextX 157 | if let n = NumberFormatter().number(from: trimStr) { 158 | 159 | //number is assumed to be in inch x 72pixels 160 | let heightIn72 = CGFloat(truncating: n) 161 | let finalHeight = heightIn72*300.0/72.0 162 | 163 | if finalHeight > 0.0 && 164 | finalHeight < selectedText.canvasSize.height 165 | { 166 | selectedText.size.height = finalHeight 167 | } 168 | 169 | if finalHeight < 80.0 170 | { 171 | selectedText.size.height = 80.0 172 | } 173 | 174 | //set to max so that behavior seems ok 175 | if finalHeight > selectedText.canvasSize.height - 20 176 | { 177 | selectedText.size.height = selectedText.canvasSize.height - 20 178 | } 179 | //update x as width changes in both directions 180 | objectPropertiesViewModel.y = String(format: "%.0f", (selectedText.location.y - CGFloat(selectedText.size.height)/2.0) * 72.0 / 300.0) 181 | 182 | print(selectedText.size.height) 183 | } 184 | } 185 | } 186 | } 187 | }//HStack 188 | 189 | 190 | 191 | } 192 | Section(header: Text("zIndex (Higher values are in front)")){ 193 | HStack{ 194 | TextField("zIndex", text: $objectPropertiesViewModel.zIndex) 195 | .onChange(of: objectPropertiesViewModel.zIndex) { newValue in 196 | self.shapes.shapeList.forEach 197 | { 198 | if $0.isSelected == true { 199 | let selectedText = $0 //as! ShapeX 200 | selectedText.zIndex = Double(newValue) ?? 0.0 201 | selectedText.zIndex = selectedText.zIndex > 100.0 ? 100.0 : selectedText.zIndex 202 | selectedText.zIndex = selectedText.zIndex < -100.0 ? -100.0 : selectedText.zIndex 203 | 204 | } 205 | 206 | } 207 | } 208 | } 209 | } 210 | } 211 | 212 | func setupViewModel() 213 | { 214 | //textPropertiesViewModel.setupSelectedProperties() 215 | 216 | } 217 | 218 | } 219 | 220 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/FilesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilesView.swift 3 | // 4 | // 5 | // Created by on 8/4/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @available(iOS 15.0, *) 11 | struct FilesViewX: View { 12 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 13 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 14 | 15 | @EnvironmentObject var optionSettings: OptionSettings 16 | @EnvironmentObject var appSettings: AppSettings 17 | @EnvironmentObject var pageSettings: PageSettings 18 | @EnvironmentObject var shapes: ShapesX 19 | @StateObject var filesViewModel: FilesViewModel 20 | @State var showFileAlert = false 21 | @State private var toBeDeleted: IndexSet? 22 | @State var showingDeleteAlert = false 23 | 24 | var body: some View { 25 | 26 | NavigationView { 27 | Form { 28 | Section(header: Text("Tap to Open or Swipe Left to Delete")){ 29 | List { 30 | ForEach(filesViewModel.files, id: \.self) { filename in 31 | Text(filename).onTapGesture { 32 | showFileAlert = true 33 | print(filename) 34 | filesViewModel.selectedFileName=filename 35 | }.swipeActions() { 36 | Button(role: .destructive) { 37 | print("delete") 38 | self.showingDeleteAlert = true 39 | filesViewModel.selectedFileName=filename 40 | } label: { 41 | Label("Delete", systemImage: "trash") 42 | } 43 | }.alert(isPresented: self.$showingDeleteAlert) { 44 | 45 | Alert(title: Text("Are you sure you want to delete this?"), message: Text("This label will be removed permanently."), primaryButton: .destructive(Text("Delete")) { 46 | let filename = filesViewModel.selectedFileName 47 | print(filename) 48 | if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { 49 | 50 | let fileURL = dir.appendingPathComponent(filename) 51 | do { 52 | try FileManager.default.removeItem(at: fileURL) 53 | } 54 | catch {} 55 | 56 | } 57 | 58 | filesViewModel.files.remove(at: filesViewModel.files.firstIndex(of: filename)!) 59 | }, secondaryButton: .cancel() { 60 | //self.toBeDeleted = nil 61 | } 62 | ) 63 | } 64 | } 65 | 66 | } 67 | .font(.subheadline) 68 | } 69 | 70 | } 71 | .navigationTitle("My Labels") 72 | .navigationBarTitleDisplayMode(.inline) 73 | } 74 | //.frame(height:290) 75 | .onAppear(perform: setupViewModel) 76 | .alert(isPresented: $showFileAlert) { 77 | Alert( 78 | title: Text("Are you sure you want to open this?"), 79 | message: Text("Your current label will be overwritten."), 80 | primaryButton: .default(Text("Open")) { 81 | //open label 82 | do { 83 | // Get the document directory url 84 | let documentDirectory = try FileManager.default.url( 85 | for: .documentDirectory, 86 | in: .userDomainMask, 87 | appropriateFor: nil, 88 | create: true 89 | ) 90 | print("documentDirectory", documentDirectory.path) 91 | 92 | let fileURL = documentDirectory.appendingPathComponent(filesViewModel.selectedFileName) 93 | 94 | let jsonStr = try String(contentsOf: fileURL, encoding: .utf8) 95 | let labelAction = LabelAction(shapes: shapes, pageSettings: pageSettings) 96 | labelAction.load(jsonLabelStr: jsonStr) 97 | appSettings.dpi=pageSettings.dpi 98 | optionSettings.showAlert = false 99 | optionSettings.showPropertiesSheet = false 100 | appSettings.zoomFactor = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.5 : 0.28 101 | 102 | } 103 | catch { 104 | print(error) 105 | } 106 | }, 107 | secondaryButton: .cancel() 108 | ) 109 | 110 | } 111 | } 112 | func share() 113 | { 114 | 115 | } 116 | 117 | func delete(at offsets: IndexSet) { 118 | 119 | self.toBeDeleted = offsets 120 | self.showingDeleteAlert = true 121 | } 122 | 123 | func setupViewModel() 124 | { 125 | do { 126 | let documentDirectory = try FileManager.default.url( 127 | for: .documentDirectory, 128 | in: .userDomainMask, 129 | appropriateFor: nil, 130 | create: true 131 | ) 132 | print("documentDirectory", documentDirectory.path) 133 | let directoryContents = try FileManager.default.contentsOfDirectory( 134 | at: documentDirectory, 135 | includingPropertiesForKeys: nil 136 | ) 137 | for url in directoryContents { 138 | print(url.lastPathComponent) 139 | if (url.lastPathComponent != "Label.pdf") 140 | { 141 | filesViewModel.files.append(url.lastPathComponent) 142 | } 143 | } 144 | } 145 | catch { 146 | print(error) 147 | } 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/ImagePickerX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // imagePicker.swift 3 | // 4 | // 5 | // Created by on 27/01/2021. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ImagePickerX: UIViewControllerRepresentable { 12 | 13 | @EnvironmentObject var shapes: ShapesX 14 | @EnvironmentObject var optionSettings: OptionSettings 15 | @Environment(\.presentationMode) var presentationMode 16 | 17 | 18 | class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 19 | 20 | var parent: ImagePickerX 21 | 22 | init(_ parent: ImagePickerX) { 23 | self.parent = parent 24 | } 25 | 26 | 27 | public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 28 | 29 | if parent.optionSettings.action == "NewImage" 30 | { 31 | var findshape: ShapeX? = nil 32 | parent.shapes.shapeList.forEach 33 | { 34 | if $0.isSelected == true { 35 | findshape = $0 36 | } 37 | } 38 | if let x = parent.shapes.shapeList.firstIndex(of: findshape!) 39 | { 40 | parent.shapes.shapeList.remove(at: x) 41 | } 42 | } 43 | 44 | print("dismis") 45 | parent.presentationMode.wrappedValue.dismiss() 46 | } 47 | 48 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 49 | 50 | print("get") 51 | if let uiImage = info[.originalImage] as? UIImage { 52 | //parent.image = uiImage 53 | 54 | parent.shapes.shapeList.forEach 55 | { 56 | if $0.isSelected == true { 57 | if $0 is ImageX 58 | { 59 | let selectedImage = $0 as! ImageX 60 | selectedImage.image = uiImage 61 | //selectedImage.width 62 | } 63 | 64 | } 65 | } 66 | 67 | } 68 | 69 | parent.presentationMode.wrappedValue.dismiss() 70 | } 71 | 72 | } 73 | 74 | func makeUIViewController(context: Context) -> UIImagePickerController { 75 | let picker = UIImagePickerController() 76 | 77 | picker.mediaTypes = ["public.image"] 78 | picker.delegate = context.coordinator 79 | return picker 80 | 81 | } 82 | 83 | 84 | func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { 85 | } 86 | 87 | func makeCoordinator() -> Coordinator { 88 | Coordinator(self) 89 | } 90 | 91 | typealias UIViewControllerType = UIImagePickerController 92 | } 93 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/ImagePropertiesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShapePropertiesX.swift 3 | // 4 | // 5 | // Created by on 25/3/22. 6 | // 7 | 8 | 9 | import SwiftUI 10 | 11 | struct ImagePropertiesViewX: View { 12 | 13 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 14 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 15 | @EnvironmentObject var optionSettings: OptionSettings 16 | @EnvironmentObject var shapes: ShapesX 17 | @StateObject var imagePropertiesViewModel: ImagePropertiesViewModel 18 | var body: some View { 19 | 20 | 21 | NavigationView { 22 | Form { 23 | 24 | DimensionsPropertiesViewX(objectPropertiesViewModel: imagePropertiesViewModel) 25 | 26 | } 27 | .navigationTitle("Image") 28 | //.navigationBarTitleDisplayMode(.inline) 29 | .toolbar { 30 | 31 | } 32 | } 33 | .navigationViewStyle(.stack) 34 | .frame(height:horizontalSizeClass == .regular && verticalSizeClass == .regular ? 400 : 290) 35 | .onAppear(perform: setupViewModel) 36 | .padding(.bottom, horizontalSizeClass == .regular && verticalSizeClass == .regular ? 70 : 0) 37 | 38 | } 39 | 40 | func setupViewModel() 41 | { 42 | imagePropertiesViewModel.setupSelectedProperties() 43 | 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/LabelDesignViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelDesignView.swift 3 | // 4 | // 5 | // Created by on 16/3/22. 6 | // 7 | 8 | import SwiftUI 9 | @available(macOS 12.0, *) 10 | @available(iOS 15.0, *) 11 | struct LabelDesignViewX: View { 12 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 13 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 14 | 15 | @State private var offsetX = CGFloat.zero 16 | @State private var offsetY = CGFloat.zero 17 | 18 | @EnvironmentObject var appSettings: AppSettings 19 | @EnvironmentObject var optionSettings: OptionSettings 20 | @EnvironmentObject var pageSettings: PageSettings 21 | @EnvironmentObject var shapes: ShapesX 22 | 23 | 24 | var body: some View { 25 | 26 | GeometryReader { geometry in 27 | VStack(spacing:0){ 28 | 29 | RulerHViewX(offsetX: $offsetX, rulerWidth: geometry.size.width) 30 | //.frame(width: geometry.size.width, height: 0.37*72.0, alignment: .topLeading) 31 | .frame(width: geometry.size.width, height: horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0, alignment: .topLeading) 32 | .border(Color.gray) 33 | .padding(0) 34 | HStack(spacing:0) 35 | { 36 | RulerVViewX(offsetY: $offsetY, rulerHeight: geometry.size.height) 37 | //.frame(width: 0.35*72.0, height: geometry.size.height, alignment: .leading) 38 | .frame(width: horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0, height: geometry.size.height, alignment: .leading) 39 | .border(Color.gray) 40 | .padding(0) 41 | VStack(spacing:0){ 42 | ScrollView([.horizontal, .vertical]) { 43 | 44 | VStack { 45 | LabelViewX() 46 | .frame(width: pageSettings.labelWidth*appSettings.dpi, height: pageSettings.labelHeight*appSettings.dpi, alignment: .center) 47 | .scaleEffect(appSettings.zoomFactor) 48 | } 49 | .frame(width: pageSettings.labelWidth*appSettings.dpi*appSettings.zoomFactor, height: pageSettings.labelHeight*appSettings.dpi*appSettings.zoomFactor, alignment: .center) 50 | .background(Color.white) 51 | .background(GeometryReader { 52 | Color.clear.preference(key: ViewOffsetKeyY.self, 53 | value: $0.frame(in: .named("scroll")).origin.y) 54 | Color.clear.preference(key: ViewOffsetKeyX.self, 55 | value: $0.frame(in: .named("scroll")).origin.x) 56 | }) 57 | .onPreferenceChange(ViewOffsetKeyY.self) { newValue in 58 | 59 | let rulerThickness = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0 60 | let labelHeight = pageSettings.labelHeight*appSettings.dpi*appSettings.zoomFactor 61 | let availableHeight = geometry.size.height - rulerThickness 62 | if offsetY != 0.0 || 63 | appSettings.zoomingOrScrollY == "scroll" || 64 | appSettings.zoomingOrScrollY == "zoomIn" || 65 | (appSettings.zoomingOrScrollY == "zoomOut" && 66 | offsetY == 0.0 && 67 | labelHeight <= availableHeight) 68 | { 69 | 70 | offsetY=newValue//$0 71 | print("assigned new valueY") 72 | 73 | } 74 | appSettings.zoomingOrScrollY = "scroll" 75 | 76 | //} 77 | } 78 | .onPreferenceChange(ViewOffsetKeyX.self) { newValue in 79 | 80 | let rulerThickness = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0 81 | let labelWidth = pageSettings.labelWidth*appSettings.dpi*appSettings.zoomFactor 82 | let availableWidth = geometry.size.width - rulerThickness 83 | 84 | if offsetX != 0.0 || 85 | appSettings.zoomingOrScrollX == "scroll" || 86 | appSettings.zoomingOrScrollX == "zoomIn" || 87 | (appSettings.zoomingOrScrollX == "zoomOut" && 88 | offsetX == 0.0 && 89 | labelWidth <= availableWidth) 90 | { 91 | offsetX=newValue//$0 92 | print("assigned new valueX") 93 | } 94 | appSettings.zoomingOrScrollX = "scroll" 95 | 96 | //} 97 | } 98 | 99 | 100 | } 101 | .coordinateSpace(name: "scroll") 102 | .background(Color.gray) 103 | .onTapGesture { 104 | shapes.shapeList.forEach 105 | { 106 | $0.isSelected = false 107 | } 108 | 109 | optionSettings.showPropertiesView=0 110 | } 111 | 112 | } 113 | .frame(height: geometry.size.height, alignment: .leading) 114 | 115 | 116 | }.padding(0) 117 | } 118 | } 119 | } 120 | } 121 | 122 | struct ViewOffsetKeyY: PreferenceKey { 123 | typealias Value = CGFloat 124 | static var defaultValue = CGFloat.zero 125 | static func reduce(value: inout Value, nextValue: () -> Value) { 126 | value += nextValue() 127 | } 128 | } 129 | 130 | struct ViewOffsetKeyX: PreferenceKey { 131 | typealias Value = CGFloat 132 | static var defaultValue = CGFloat.zero 133 | static func reduce(value: inout Value, nextValue: () -> Value) { 134 | value += nextValue() 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/LabelMainViewX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | import SwiftUI 11 | import CoreImage 12 | import CoreImage.CIFilterBuiltins 13 | import UniformTypeIdentifiers 14 | 15 | @available(iOS 15.0, *) 16 | 17 | 18 | struct LabelMainViewX: View { 19 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 20 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 21 | @EnvironmentObject var optionSettings: OptionSettings 22 | @EnvironmentObject var appSettings: AppSettings 23 | @EnvironmentObject var pageSettings: PageSettings 24 | @EnvironmentObject var shapes: ShapesX 25 | 26 | //https://stackoverflow.com/questions/57577462/get-width-of-a-view-using-in-swiftui 27 | //https://developer.apple.com/forums/thread/123012 28 | 29 | private func binding(for shape: ShapeX) -> Binding { 30 | guard let scrumIndex = shapes.shapeList.firstIndex(where: { $0.id == shape.id }) else { 31 | fatalError("Can't find scrum in array") 32 | } 33 | return $shapes.shapeList[scrumIndex] 34 | } 35 | 36 | var body: some View { 37 | NavigationView { 38 | VStack { 39 | LabelDesignViewX() 40 | .environmentObject(optionSettings) 41 | .environmentObject(pageSettings) 42 | .environmentObject(appSettings) 43 | .environmentObject(shapes) 44 | MainPropertiesViewX() 45 | .environmentObject(optionSettings) 46 | .environmentObject(pageSettings) 47 | .environmentObject(appSettings) 48 | .environmentObject(shapes) 49 | } 50 | .navigationTitle("Barcode & Label") 51 | .navigationBarTitleDisplayMode(.inline) 52 | .toolbar { 53 | ToolbarItemGroup(placement: .navigationBarLeading) { 54 | PageToolbarViewX() 55 | } 56 | ToolbarItemGroup(placement: .navigationBarTrailing) { 57 | OptionsPrintToolbarViewX() 58 | } 59 | } 60 | .ignoresSafeArea(.container, edges: [.bottom]) 61 | } //navigationview 62 | .navigationViewStyle(StackNavigationViewStyle()) 63 | .onAppear(perform: initAll) 64 | .alert(isPresented: $optionSettings.showingAlertMessage) { 65 | Alert(title: Text("Status"), message: Text(optionSettings.alertMessage!), dismissButton: .default(Text("OK"))) 66 | } 67 | .sheet(isPresented: $optionSettings.showPropertiesSheet, onDismiss: dismissSheet) { 68 | 69 | MainSheetViewX() 70 | .environmentObject(appSettings) 71 | .environmentObject(optionSettings) 72 | .environmentObject(pageSettings) 73 | .environmentObject(shapes) 74 | 75 | 76 | } //sheet 77 | .textFieldAlert(isPresented: $optionSettings.showAlert) { () -> TextFieldAlert in 78 | TextFieldAlert(title: "Saving Label", 79 | message: "Please specify label name.", 80 | labelContent:optionSettings.jsonLabelStringForSave, 81 | text: $optionSettings.enteredSaveFileName, 82 | saveSuccess: $optionSettings.showingAlertMessage, 83 | existingLabelExist: $optionSettings.existingLabelExist, 84 | alertMessage: $optionSettings.alertMessage) 85 | } 86 | .fileImporter( 87 | isPresented: $optionSettings.isImporting, 88 | allowedContentTypes: [UTType.json], 89 | allowsMultipleSelection: false 90 | ) { result in 91 | do { 92 | guard let selectedFile: URL = try result.get().first else { return } 93 | guard selectedFile.startAccessingSecurityScopedResource() else { return } 94 | guard let message = String(data: try Data(contentsOf: selectedFile), encoding: .utf8) else { return } 95 | let labelAction = LabelAction(shapes: shapes, pageSettings: pageSettings) 96 | labelAction.load(jsonLabelStr: message) 97 | optionSettings.showAlert = false 98 | optionSettings.showPropertiesSheet = false 99 | appSettings.zoomFactor = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.5 : 0.28 100 | 101 | selectedFile.stopAccessingSecurityScopedResource() 102 | optionSettings.showingAlertMessage = true 103 | optionSettings.alertMessage="Label imported successfully." 104 | //import and open appSettings.dpi use the pageSttings one which is the onve saved 105 | appSettings.dpi=pageSettings.dpi 106 | 107 | } catch { 108 | print(error.localizedDescription) 109 | optionSettings.showingAlertMessage = true 110 | optionSettings.alertMessage="An error has occured while importing label - Invalid Label File." 111 | } 112 | } 113 | .fileExporter( 114 | isPresented: $optionSettings.isExporting, 115 | document: optionSettings.labelDocument, 116 | contentType: UTType.json, 117 | defaultFilename: "Label" 118 | ) { result in 119 | optionSettings.showingAlertMessage = true 120 | if case .success = result { 121 | optionSettings.alertMessage="Label exported successfully." 122 | print("Success!") 123 | } else { 124 | optionSettings.alertMessage="An error has occured while exporting label. Please try again or contact us." 125 | print("Something went wrong…") 126 | } 127 | } 128 | 129 | } //body 130 | 131 | 132 | func dismissSheet() 133 | { 134 | optionSettings.action = "Design" 135 | } 136 | 137 | func initLabel() { 138 | shapes.shapeList=[ShapeX]() 139 | } 140 | 141 | func initPage() { 142 | print("initPage:",labelTemplatesAll.count) 143 | 144 | //new and startup will use appSettings dpi 145 | pageSettings.dpi=appSettings.dpi 146 | 147 | pageSettings.name = labelTemplates[8][0] 148 | pageSettings.category = labelTemplates[8][1] 149 | pageSettings.vendor = labelTemplates[8][2] 150 | pageSettings.description = labelTemplates[8][3] 151 | pageSettings.type = labelTemplates[8][4] 152 | pageSettings.pageWidth = Double(labelTemplates[8][5]) ?? 1.0 153 | pageSettings.pageHeight = Double(labelTemplates[8][6]) ?? 1.0 154 | pageSettings.labelWidth = Double(labelTemplates[8][7]) ?? 1.0 155 | pageSettings.labelHeight = Double(labelTemplates[8][8]) ?? 1.0 156 | pageSettings.hSpace = Double(labelTemplates[8][9]) ?? 0.0 157 | pageSettings.vSpace = Double(labelTemplates[8][10]) ?? 0.0 158 | pageSettings.numRows = Int(labelTemplates[8][11]) ?? 1 159 | pageSettings.numCols = Int(labelTemplates[8][12]) ?? 1 160 | pageSettings.leftMargin = Double(labelTemplates[8][13]) ?? 0.0 161 | pageSettings.topMargin = Double(labelTemplates[8][14]) ?? 0.0 162 | pageSettings.generateLabels() 163 | 164 | let defaults = UserDefaults.standard 165 | if let unitstring = defaults.string(forKey: "units"), !unitstring.isEmpty { 166 | print("Getting:",unitstring) 167 | appSettings.units = unitstring 168 | } 169 | else{ 170 | print("Setting:",appSettings.units) 171 | defaults.set(appSettings.units, forKey: "units") 172 | } 173 | 174 | 175 | } 176 | func convertStringToDictionary(text: String) -> [String:AnyObject]? { 177 | if let data = text.data(using: .utf8) { 178 | do { 179 | let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject] 180 | return json 181 | } catch { 182 | print("Something went wrong") 183 | } 184 | } 185 | return nil 186 | } 187 | 188 | func initAll() 189 | { 190 | initLabel() 191 | initPage() 192 | 193 | 194 | //let timer2 = 195 | Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { timer in 196 | print("Timer fired!") 197 | appSettings.zoomFactor = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.5 : 0.28 198 | print(appSettings.zoomFactor) 199 | } 200 | 201 | } 202 | } 203 | 204 | 205 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/LabelViewX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | struct LabelViewX: View { 14 | 15 | 16 | @EnvironmentObject var optionSettings: OptionSettings 17 | @EnvironmentObject var appSettings: AppSettings 18 | @EnvironmentObject var pageSettings: PageSettings 19 | @EnvironmentObject var shapes: ShapesX 20 | 21 | var body: some View { 22 | 23 | ZStack { 24 | 25 | ForEach(shapes.shapeList){ 26 | shape in 27 | ShapeViewX(shape: shape, showPropertiesView: $optionSettings.showPropertiesView) 28 | } 29 | 30 | } 31 | .background(Color.white) 32 | .onTapGesture { 33 | shapes.shapeList.forEach 34 | { 35 | if ($0.isSelected == true) 36 | { 37 | print("set false") 38 | $0.isSelected = false 39 | } 40 | } 41 | optionSettings.showPropertiesView = 0 42 | } 43 | .clipShape(Rectangle()) 44 | //.gesture(pressGesture) 45 | 46 | } 47 | 48 | 49 | } 50 | 51 | //https://stackoverflow.com/questions/70032739/why-does-swiftui-uihostingcontroller-have-extra-spacing 52 | extension UIHostingController { 53 | convenience public init(rootView: Content, ignoreSafeArea: Bool) { 54 | self.init(rootView: rootView) 55 | 56 | if ignoreSafeArea { 57 | disableSafeArea() 58 | } 59 | } 60 | 61 | func disableSafeArea() { 62 | guard let viewClass = object_getClass(view) else { return } 63 | 64 | let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea") 65 | if let viewSubclass = NSClassFromString(viewSubclassName) { 66 | object_setClass(view, viewSubclass) 67 | } 68 | else { 69 | guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } 70 | guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } 71 | 72 | if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) { 73 | let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in 74 | return .zero 75 | } 76 | class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method)) 77 | } 78 | 79 | objc_registerClassPair(viewSubclass) 80 | object_setClass(view, viewSubclass) 81 | } 82 | } 83 | } 84 | 85 | extension CGSize { 86 | static func * (size: CGSize, value: CGFloat) -> CGSize { 87 | return CGSize(width: size.width * value, height: size.height * value) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/MainPropertiesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainSheetViewX.swift 3 | // 4 | // 5 | // Created by on 21/4/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @available(iOS 15.0, *) 11 | struct MainPropertiesViewX: View { 12 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 13 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 14 | 15 | @EnvironmentObject var optionSettings: OptionSettings 16 | @EnvironmentObject var appSettings: AppSettings 17 | @EnvironmentObject var pageSettings: PageSettings 18 | @EnvironmentObject var shapes: ShapesX 19 | 20 | var body: some View { 21 | VStack{ 22 | 23 | if optionSettings.showPropertiesView == 2 24 | { 25 | ForEach(shapes.shapeList){ 26 | shape in 27 | if shape.isSelected == true && shape is TextX 28 | { 29 | TextPropertiesViewX(textPropertiesViewModel: TextPropertiesViewModel(shapes: shapes)) 30 | } 31 | } 32 | } 33 | else if optionSettings.showPropertiesView == 3 34 | { 35 | ForEach(shapes.shapeList){ 36 | shape in 37 | if shape.isSelected == true && (shape is RectangleX || shape is EllipseX) 38 | { 39 | ShapePropertiesViewX(shapePropertiesViewModel: ShapePropertiesViewModel(shapes: shapes)) 40 | } 41 | } 42 | } 43 | else if optionSettings.showPropertiesView == 5 44 | { 45 | ForEach(shapes.shapeList){ 46 | shape in 47 | if shape.isSelected == true && shape is BarcodeX 48 | { 49 | BarcodePropertiesViewX(barcodePropertiesViewModel: BarcodePropertiesViewModel(shapes: shapes)) 50 | } 51 | } 52 | } 53 | else if optionSettings.showPropertiesView == 6 54 | { 55 | ForEach(shapes.shapeList){ 56 | shape in 57 | if shape.isSelected == true && shape is ImageX 58 | { 59 | ImagePropertiesViewX(imagePropertiesViewModel: ImagePropertiesViewModel(shapes: shapes)) 60 | } 61 | } 62 | } 63 | else 64 | { 65 | VStack (alignment: .trailing) 66 | { 67 | HStack{ 68 | LabelToolbarViewX() 69 | }.frame(height: horizontalSizeClass == .regular && verticalSizeClass == .regular ? 85 : 85) 70 | 71 | }.frame(height: horizontalSizeClass == .regular && verticalSizeClass == .regular ? 85 : 85) 72 | } 73 | 74 | 75 | //Spacer() //for toolbar push up 76 | } 77 | .frame(height: (optionSettings.showPropertiesView != 0 ? 280 : 78 | (horizontalSizeClass == .regular && verticalSizeClass == .regular ? 85 : 85))) //so as to extend beyond the bottomn toolbar 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/MainSheetViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainSheetViewX.swift 3 | // 4 | // 5 | // Created by on 21/4/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @available(iOS 15.0, *) 11 | struct MainSheetViewX: View { 12 | @EnvironmentObject var optionSettings: OptionSettings 13 | @EnvironmentObject var appSettings: AppSettings 14 | @EnvironmentObject var pageSettings: PageSettings 15 | @EnvironmentObject var shapes: ShapesX 16 | 17 | var body: some View { 18 | if optionSettings.action == "NewLabel" 19 | { 20 | 21 | TemplateViewX(templateViewModel: TemplateViewModel(units: appSettings.units, pageSettings: self.pageSettings)) 22 | .environmentObject(appSettings) 23 | .environmentObject(optionSettings) 24 | .environmentObject(pageSettings) 25 | .environmentObject(shapes) 26 | 27 | } 28 | else if optionSettings.action == "OpenLabel" 29 | { 30 | FilesViewX(filesViewModel: FilesViewModel()) 31 | .environmentObject(optionSettings) 32 | } 33 | else if optionSettings.action == "Preview" 34 | { 35 | PreviewViewX(previewViewModel: PreviewViewModel(dpi: appSettings.dpi,pageType: pageSettings.type)) 36 | .environmentObject(appSettings) 37 | .environmentObject(optionSettings) 38 | .environmentObject(pageSettings) 39 | .environmentObject(appSettings) 40 | .environmentObject(shapes) 41 | } 42 | else if optionSettings.action == "NewImage" || optionSettings.action == "EditImage" { 43 | 44 | ImagePickerX() 45 | .environmentObject(optionSettings) 46 | .environmentObject(shapes) 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/OptionsPrintToolbarViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaeblToolbarView.swift 3 | // 4 | // 5 | // Created by on 17/3/22. 6 | // 7 | 8 | import SwiftUI 9 | @available(iOS 15.0, *) 10 | struct OptionsPrintToolbarViewX: View { 11 | @Environment(\.colorScheme) var colorScheme 12 | @EnvironmentObject var appSettings: AppSettings 13 | @EnvironmentObject var optionSettings: OptionSettings 14 | @EnvironmentObject var pageSettings: PageSettings 15 | @EnvironmentObject var shapes: ShapesX 16 | @State private var showingAlert = false 17 | 18 | @State var measurementUnit : String = "Inches" 19 | 20 | var body: some View { 21 | 22 | Button(action: { 23 | 24 | shapes.deSelectAll(); 25 | 26 | optionSettings.action = "Preview" 27 | //come back and change the name to showSheet 28 | optionSettings.showPropertiesSheet = true 29 | 30 | }, label: { 31 | Label("Print", systemImage: "printer") 32 | //.font(.system(size: 16)) 33 | }).foregroundColor(.black) 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/PageToolbarViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaeblToolbarView.swift 3 | // 4 | // 5 | // Created by on 17/3/22. 6 | // 7 | 8 | import SwiftUI 9 | import MobileCoreServices 10 | @available(iOS 15.0, *) 11 | 12 | @available(iOS 15.0, *) 13 | struct PageToolbarViewX: View { 14 | @Environment(\.colorScheme) var colorScheme 15 | @EnvironmentObject var appSettings: AppSettings 16 | @EnvironmentObject var optionSettings: OptionSettings 17 | @EnvironmentObject var pageSettings: PageSettings 18 | @EnvironmentObject var shapes: ShapesX 19 | @State private var showingAlert = false 20 | 21 | @State var measurementUnit : String = "Inches" 22 | 23 | 24 | 25 | var body: some View { 26 | 27 | Menu { 28 | 29 | Button(action: { 30 | 31 | shapes.deSelectAll(); 32 | 33 | }, label: { 34 | Label("Labels", systemImage: "folder") 35 | 36 | 37 | }) 38 | .contextMenu { 39 | Button(action: { 40 | shapes.deSelectAll(); 41 | optionSettings.action = "NewLabel" 42 | 43 | optionSettings.showPropertiesSheet = true 44 | 45 | 46 | 47 | }) { 48 | Label("New", systemImage: "doc.badge.plus") 49 | } 50 | 51 | Button(action: { 52 | shapes.deSelectAll(); 53 | optionSettings.action = "OpenLabel" 54 | /* 55 | pageSettings.dpi=72.0 56 | //pageSettings.generateLabels() 57 | */ 58 | optionSettings.showPropertiesSheet = true 59 | 60 | 61 | }) { 62 | //Label("Open Label", systemImage: "doc.badge.gearshape") 63 | Label("My Labels", systemImage: "arrow.up.doc") 64 | } 65 | 66 | 67 | 68 | Button(action: { 69 | shapes.deSelectAll(); 70 | optionSettings.action = "SaveLabel" 71 | 72 | let labelAction = LabelAction(shapes: shapes, pageSettings: pageSettings) 73 | optionSettings.jsonLabelStringForSave = labelAction.generate() 74 | optionSettings.showAlert = true 75 | 76 | 77 | }) { 78 | Label("Save", systemImage: "arrow.down.doc") 79 | } 80 | 81 | Button(action: { 82 | print("importing") 83 | shapes.deSelectAll(); 84 | optionSettings.action = "Import" 85 | optionSettings.isImporting = true 86 | }) { 87 | Label("Import", systemImage: "square.and.arrow.down") 88 | } 89 | 90 | Button(action: { 91 | print("exporting") 92 | 93 | let labelAction = LabelAction(shapes: shapes, pageSettings: pageSettings) 94 | optionSettings.labelDocument.message = labelAction.generate() 95 | 96 | 97 | optionSettings.action = "Export" 98 | optionSettings.isExporting = true 99 | }) { 100 | Label("Export", systemImage: "square.and.arrow.up") 101 | } 102 | 103 | 104 | } 105 | 106 | Button(action: { 107 | 108 | shapes.deSelectAll(); 109 | 110 | }, label: { 111 | Label("Options", systemImage: "doc.badge.gearshape") 112 | 113 | 114 | }) 115 | .contextMenu { 116 | 117 | Menu("Measurements") { 118 | Picker("Units", selection: $appSettings.units) { 119 | ForEach(measurementUnits, id: \.self) { 120 | Text($0) 121 | } 122 | } 123 | .onChange(of: appSettings.units) { newValue in 124 | let defaults = UserDefaults.standard 125 | print("Setting:",appSettings.units) 126 | defaults.set(appSettings.units, forKey: "units") 127 | 128 | } 129 | } 130 | 131 | Menu("Help") { 132 | Link("Help", destination: URL(string: "https://www.barcoderesource.com/barcodelabelappleapp.shtml")!) 133 | .font(.title) 134 | //.foregroundColor(.red) 135 | Link("Open Source", destination: URL(string: "https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml")!) 136 | .font(.title) 137 | //.foregroundColor(.red) 138 | } 139 | 140 | } 141 | 142 | 143 | 144 | 145 | } 146 | label: { 147 | VStack { 148 | 149 | Image(systemName: "doc") 150 | .foregroundColor(colorScheme == .dark ? Color(UIColor(red: 0.9569, green: 0.9569, blue: 0.9569, alpha: 0.5) ) : Color.black) 151 | } 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/RulerHViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RulerH.swift 3 | // 4 | // 5 | // Created by on 15/3/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @available(macOS 12.0, *) 11 | @available(iOS 15.0, *) 12 | struct RulerHViewX: View { 13 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 14 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 15 | 16 | @Binding var offsetX: CGFloat 17 | var rulerWidth: CGFloat 18 | @Environment(\.colorScheme) var colorScheme 19 | @EnvironmentObject var appSettings: AppSettings 20 | @EnvironmentObject var pageSettings: PageSettings 21 | var body: some View { 22 | Canvas { context, size in 23 | 24 | var dpi = 72.0*appSettings.dpiScale 25 | if appSettings.units != "Inches" 26 | { 27 | dpi = dpi / 2.54 28 | } 29 | //let widthx = 2.625*72.0*appSettings.dpiScale*appSettings.zoomFactor 30 | let widthx = pageSettings.labelWidth*72.0*appSettings.dpiScale*appSettings.zoomFactor 31 | 32 | //let thickness = 0.37*72.0 33 | let thickness = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0 34 | 35 | let spacingx = 0.1*dpi*appSettings.zoomFactor 36 | let countx = widthx / spacingx 37 | let bottomy = thickness 38 | let heightSmallTicks = 0.1*72.0 39 | 40 | let spacingxBig = 1.0*dpi*appSettings.zoomFactor 41 | let countxBig = widthx / spacingxBig 42 | let bottomyBig = thickness 43 | let heightSmallTicksBig = 0.2*72.0 44 | 45 | let spacingxMed = 1.0*dpi*appSettings.zoomFactor 46 | let countxMed = widthx / spacingxMed 47 | let bottomyMed = thickness 48 | let heightSmallTicksMed = 0.15*72.0 49 | 50 | 51 | 52 | var currentx = 0.0 53 | 54 | var countzero = (offsetX + thickness) / spacingx 55 | //var darkColor: UIColor = UIColor.black 56 | let path = CGMutablePath() 57 | let xpath = CGMutablePath() 58 | currentx = offsetX + thickness 59 | countzero = countzero < 0.0 ? 0.0 : countzero 60 | for _ in 0...Int(countzero) 61 | { 62 | if (spacingx > 2.0) //prevent too small space between small ticks in centimeters 63 | { 64 | if currentx > 0.0 65 | { 66 | xpath.move(to: CGPoint(x: currentx, y: bottomy)) 67 | xpath.addLine(to: CGPoint(x: currentx, y: bottomy-heightSmallTicks)) 68 | currentx -= spacingx 69 | } 70 | } 71 | } 72 | 73 | let storecurrentx = currentx 74 | 75 | currentx = offsetX + thickness 76 | 77 | for _ in 0...Int(countx) 78 | { 79 | if (spacingx > 2.0) //prevent too small space between small ticks in centimeters 80 | { 81 | path.move(to: CGPoint(x: currentx, y: bottomy)) 82 | path.addLine(to: CGPoint(x: currentx, y: bottomy-heightSmallTicks)) 83 | currentx += spacingx 84 | } 85 | } 86 | 87 | currentx = offsetX + thickness + 0.0*dpi*appSettings.zoomFactor 88 | 89 | 90 | for tx in 0...Int(countxBig)+1 91 | { 92 | 93 | let textPoint = CGPoint(x: currentx, y: bottomyBig-heightSmallTicksBig-3) 94 | let text = Text(String(tx)).font(.system(size: 11)) 95 | context.draw(text, at: textPoint, anchor: .center) 96 | 97 | path.move(to: CGPoint(x: currentx, y: bottomyBig)) 98 | path.addLine(to: CGPoint(x: currentx, y: bottomyBig-heightSmallTicksBig+3)) 99 | currentx += spacingxBig 100 | } 101 | 102 | currentx = offsetX + thickness + 0.5*dpi*appSettings.zoomFactor 103 | for _ in 0...Int(countxMed) 104 | { 105 | path.move(to: CGPoint(x: currentx, y: bottomyMed)) 106 | path.addLine(to: CGPoint(x: currentx, y: bottomyBig-heightSmallTicksMed)) 107 | currentx += spacingxMed 108 | } 109 | 110 | context.stroke( 111 | Path(path), 112 | with: .color(.black), 113 | lineWidth: 1) 114 | 115 | currentx = storecurrentx 116 | var countend = (rulerWidth - currentx) / spacingx 117 | countend = countend < 0.0 ? 0.0 : countend 118 | 119 | for _ in 0...Int(countend) 120 | { 121 | if (spacingx > 2.0) //prevent too small space between small ticks in centimeters 122 | { 123 | 124 | xpath.move(to: CGPoint(x: currentx, y: bottomy)) 125 | xpath.addLine(to: CGPoint(x: currentx, y: bottomy-heightSmallTicks)) 126 | currentx += spacingx 127 | } 128 | } 129 | context.stroke( 130 | Path(xpath), 131 | with: .color(.black), 132 | lineWidth: 1.0) 133 | 134 | }.background(colorScheme == .dark ? Color(UIColor.lightGray) : Color.white) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/RulerVViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RulerVView.swift 3 | // 4 | // 5 | // Created by on 15/3/22. 6 | // 7 | 8 | import SwiftUI 9 | @available(iOS 15.0, *) 10 | struct RulerVViewX: View { 11 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 12 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 13 | 14 | @Binding var offsetY: CGFloat 15 | var rulerHeight: CGFloat 16 | @Environment(\.colorScheme) var colorScheme 17 | @EnvironmentObject var appSettings: AppSettings 18 | @EnvironmentObject var pageSettings: PageSettings 19 | var body: some View { 20 | Canvas { context, size in 21 | 22 | var dpi = 72.0*appSettings.dpiScale 23 | if appSettings.units != "Inches" 24 | { 25 | dpi = dpi / 2.54 26 | } 27 | //let heighty = 1.0*72.0*appSettings.dpiScale*appSettings.zoomFactor 28 | let heighty = pageSettings.labelHeight*72.0*appSettings.dpiScale*appSettings.zoomFactor 29 | //let thickness = 0.35*72.0 30 | let thickness = horizontalSizeClass == .regular && verticalSizeClass == .regular ? 0.4*72.0 : 0.38*72.0 31 | //let heighty = 11*72.0 32 | let spacingy = 0.1*dpi*appSettings.zoomFactor 33 | let countx = heighty / spacingy 34 | //let rightx = thickness 35 | let heightSmallTicks = 0.1*72.0 36 | 37 | 38 | let spacingyBig = 1.0*dpi*appSettings.zoomFactor 39 | let countxBig = heighty / spacingyBig 40 | //print("countxBig:",countxBig,":",heighty,":",spacingyBig) 41 | //let rightxBig = thickness 42 | let heightBigTicks = 0.2*72.0 43 | 44 | let spacingyMed = 1.0*dpi*appSettings.zoomFactor 45 | let countxMed = heighty / spacingyMed 46 | //let rightxMed = thickness 47 | let heightMedTicks = 0.155*72.0 48 | 49 | //let pixelAdjust = 1.0 50 | //let stackSpaceAdjust = 2.0 51 | 52 | 53 | var countzero = offsetY / spacingy 54 | let xpath = CGMutablePath() 55 | 56 | countzero = countzero < 0.0 ? 0.0 : countzero 57 | var currenty = offsetY 58 | for _ in 0...Int(countzero) 59 | { 60 | if (spacingy > 2.0) //prevent too small space between small ticks in centimeters 61 | { 62 | if currenty > 0.0 63 | { 64 | xpath.move(to: CGPoint(x: thickness, y: currenty)) 65 | xpath.addLine(to: CGPoint(x: thickness-heightSmallTicks , y: currenty)) 66 | currenty -= spacingy 67 | } 68 | } 69 | } 70 | 71 | 72 | //var initialy = thickness + spacingy + stackSpaceAdjust + pixelAdjust //2 is the vstack spacing betwwen reular and displayview 73 | //var initialy = offsetY + 0.155*72.0 //+ thickness 74 | var initialy = offsetY 75 | //1 is a little adjustment 76 | currenty = initialy 77 | //var darkColor: UIColor = UIColor.black 78 | let path = CGMutablePath() 79 | 80 | for _ in 1...Int(countx) 81 | { 82 | if (spacingy > 2.0) //prevent too small space between small ticks in centimeters 83 | { 84 | path.move(to: CGPoint(x: thickness, y: currenty)) 85 | path.addLine(to: CGPoint(x: thickness-heightSmallTicks , y: currenty)) 86 | currenty += spacingy 87 | } 88 | } 89 | let storecurrenty=currenty 90 | 91 | //initialy = thickness + stackSpaceAdjust + pixelAdjust 92 | //initialy = offsetY + 0.155*72.0 //+ thickness 93 | initialy = offsetY 94 | currenty = initialy 95 | 96 | print("thickness:",thickness) 97 | print("offsetY:",offsetY) 98 | print("currenty:",currenty) 99 | 100 | for ty in 0...Int(countxBig)+1 101 | { 102 | 103 | let textPoint = CGPoint(x: thickness-heightBigTicks-6, y: currenty) 104 | let text = Text(String(ty)).font(.system(size: 11)) 105 | context.draw(text, at: textPoint, anchor: .center) 106 | 107 | path.move(to: CGPoint(x: thickness, y: currenty)) 108 | path.addLine(to: CGPoint(x: thickness-heightBigTicks , y: currenty)) 109 | currenty += spacingyBig 110 | } 111 | 112 | let initialOffsetDueToRulerH = 0.5*dpi*appSettings.zoomFactor 113 | 114 | 115 | initialy = offsetY + initialOffsetDueToRulerH 116 | 117 | currenty = initialy 118 | 119 | for _ in 0...Int(countxMed) 120 | { 121 | path.move(to: CGPoint(x: thickness, y: currenty)) 122 | path.addLine(to: CGPoint(x: thickness-heightMedTicks , y: currenty)) 123 | currenty += spacingyMed 124 | } 125 | 126 | context.stroke( 127 | Path(path), 128 | with: .color(.black), 129 | lineWidth: 1) 130 | 131 | currenty = storecurrenty 132 | var countend = (rulerHeight - currenty) / spacingy 133 | //countend = countend < 0.0 ? 0.0 : countend 134 | countend = countend < 1.0 ? 1.0 : countend 135 | for _ in 1...Int(countend) 136 | { 137 | if (spacingy > 2.0) //prevent too small space between small ticks in centimeters 138 | { 139 | xpath.move(to: CGPoint(x: thickness, y: currenty)) 140 | xpath.addLine(to: CGPoint(x: thickness-heightSmallTicks , y: currenty)) 141 | currenty += spacingy 142 | } 143 | } 144 | 145 | context.stroke( 146 | Path(xpath), 147 | with: .color(.black), 148 | lineWidth: 1.0) 149 | }.background(colorScheme == .dark ? Color(UIColor.lightGray) : Color.white) 150 | } 151 | } 152 | 153 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/ShapePropertiesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShapePropertiesX.swift 3 | // 4 | // 5 | // Created by on 25/3/22. 6 | // 7 | 8 | 9 | import SwiftUI 10 | 11 | struct ShapePropertiesViewX: View { 12 | 13 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 14 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 15 | @EnvironmentObject var optionSettings: OptionSettings 16 | @EnvironmentObject var shapes: ShapesX 17 | @StateObject var shapePropertiesViewModel: ShapePropertiesViewModel 18 | var body: some View { 19 | 20 | 21 | NavigationView { 22 | Form { 23 | 24 | 25 | Section(header: Text("Stroke")){ 26 | 27 | HStack() { 28 | Text("Width") 29 | Spacer() 30 | Text(String(format: "%.0f", round(Double(shapePropertiesViewModel.strokeWidth * 72.0/300.0)+1))) 31 | //Text("\(Double(shapePropertiesViewModel.strokeWidth * 72.0/300.0))") 32 | } 33 | Slider(value: $shapePropertiesViewModel.strokeWidth, in: 0.2*300.0/72.0...29*300.0/72.0) 34 | .onChange(of: shapePropertiesViewModel.strokeWidth) { newValue in 35 | //print("sw changed to \(sw)!") 36 | //print("change") 37 | //print(newValue) 38 | shapes.shapeList.forEach 39 | { 40 | if $0.isSelected == true { 41 | if $0 is RectangleX 42 | { 43 | let selectedRect = $0 as! RectangleX 44 | selectedRect.strokeWidth=newValue 45 | } 46 | else if $0 is EllipseX 47 | { 48 | let selectedRect = $0 as! EllipseX 49 | selectedRect.strokeWidth=newValue 50 | } 51 | } 52 | } 53 | } 54 | HStack{ 55 | Text("Color") 56 | Spacer() 57 | ColorPicker("", selection: $shapePropertiesViewModel.strokeColor, supportsOpacity: false) 58 | .onChange(of: shapePropertiesViewModel.strokeColor) { newValue in 59 | 60 | self.shapes.shapeList.forEach 61 | { 62 | if $0.isSelected == true { 63 | if $0 is RectangleX 64 | { 65 | let selectedRect = $0 as! RectangleX 66 | selectedRect.strokeColor = newValue 67 | } 68 | else if $0 is EllipseX 69 | { 70 | let selectedRect = $0 as! EllipseX 71 | selectedRect.strokeColor = newValue 72 | } 73 | } 74 | } 75 | }.frame(width:100, height:30, alignment: .trailing) 76 | } 77 | 78 | } 79 | Section(header: Text("Fill")){ 80 | 81 | HStack{ 82 | Text("Color") 83 | Spacer() 84 | ColorPicker("", selection: $shapePropertiesViewModel.fillColor, supportsOpacity: false) 85 | .onChange(of: shapePropertiesViewModel.fillColor) { newValue in 86 | 87 | self.shapes.shapeList.forEach 88 | { 89 | if $0.isSelected == true { 90 | 91 | if $0 is RectangleX 92 | { 93 | let selectedRect = $0 as! RectangleX 94 | selectedRect.fillColor = newValue 95 | } 96 | else if $0 is EllipseX 97 | { 98 | let selectedRect = $0 as! EllipseX 99 | selectedRect.fillColor = newValue 100 | } 101 | } 102 | 103 | } 104 | }.frame(width:100, height:30, alignment: .trailing) 105 | } 106 | 107 | } 108 | 109 | DimensionsPropertiesViewX(objectPropertiesViewModel: shapePropertiesViewModel) 110 | 111 | 112 | } 113 | .navigationTitle("Shape") 114 | //.navigationBarTitleDisplayMode(.inline) 115 | .toolbar { 116 | 117 | } 118 | } 119 | .navigationViewStyle(.stack) 120 | .frame(height:horizontalSizeClass == .regular && verticalSizeClass == .regular ? 400 : 290) 121 | .onAppear(perform: setupViewModel) 122 | .padding(.bottom, horizontalSizeClass == .regular && verticalSizeClass == .regular ? 70 : 0) 123 | } 124 | 125 | func setupViewModel() 126 | { 127 | shapePropertiesViewModel.setupSelectedProperties() 128 | 129 | } 130 | } 131 | 132 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/TemplateLabelViewX.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | import SwiftUI 12 | 13 | struct TemplateLabelViewX: View { 14 | 15 | 16 | var body: some View { 17 | 18 | ZStack { 19 | 20 | 21 | } 22 | .background(Color.white) 23 | .onTapGesture { 24 | 25 | } 26 | 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Shared/BarcodeLabel/Views/TextPropertiesViewX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextPropertiesView.swift 3 | // (iOS) 4 | // 5 | // Created by on 23/4/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TextPropertiesViewX: View { 11 | 12 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? 13 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? 14 | @EnvironmentObject var optionSettings: OptionSettings 15 | @EnvironmentObject var shapes: ShapesX 16 | @StateObject var textPropertiesViewModel: TextPropertiesViewModel 17 | @State private var letter = "" 18 | private let letters = ["Alpha", "Bravo", "Charlie"] 19 | 20 | var body: some View { 21 | 22 | NavigationView { 23 | Form { 24 | Section(header: Text("Text")){ 25 | 26 | HStack{ 27 | 28 | 29 | TextField("Text", text: $textPropertiesViewModel.text) 30 | .onChange(of: textPropertiesViewModel.text) { newValue in 31 | 32 | self.shapes.shapeList.forEach 33 | { 34 | if $0.isSelected == true { 35 | let selectedText = $0 as! TextX 36 | 37 | if newValue.count > 200 38 | { 39 | selectedText.text = String(newValue.prefix(200)) 40 | } 41 | else{ 42 | selectedText.text = newValue 43 | } 44 | 45 | } 46 | } 47 | } 48 | //.padding(5).textFieldStyle(RoundedBorderTextFieldStyle()) 49 | } 50 | } 51 | 52 | Section(header: Text("Font")){ 53 | 54 | Picker("Name", selection: $textPropertiesViewModel.fontName) { 55 | ForEach(fontNames, id: \.self) { 56 | Text($0) 57 | } 58 | } 59 | .onChange(of: textPropertiesViewModel.fontName) { newValue in 60 | self.shapes.shapeList.forEach 61 | { 62 | if $0.isSelected == true { 63 | let selectedText = $0 as! TextX 64 | selectedText.fontName = newValue 65 | } 66 | } 67 | } 68 | HStack{ 69 | Text("Color") 70 | Spacer() 71 | ColorPicker("", selection: $textPropertiesViewModel.textColor, supportsOpacity: false) 72 | .onChange(of: textPropertiesViewModel.textColor) { newValue in 73 | 74 | self.shapes.shapeList.forEach 75 | { 76 | if $0.isSelected == true { 77 | let selectedText = $0 as! TextX 78 | selectedText.textColor = newValue 79 | } 80 | } 81 | }.frame(width:100, height:30, alignment: .trailing) 82 | } 83 | HStack{ 84 | Text("Size") 85 | Spacer() 86 | 87 | 88 | Text("\(Int(textPropertiesViewModel.fontSize * 72.0/300.0))") 89 | 90 | } 91 | Slider(value: $textPropertiesViewModel.fontSize, in: 10...600 ) 92 | .onChange(of: textPropertiesViewModel.fontSize) { newValue in 93 | self.shapes.shapeList.forEach 94 | { 95 | if $0.isSelected == true { 96 | let selectedText = $0 as! TextX 97 | selectedText.fontSize = newValue 98 | 99 | } 100 | } 101 | } 102 | //Spacer() 103 | } 104 | Section(header: Text("Horizontal Text Alignment")){ 105 | Picker("Text Alignment", selection: $textPropertiesViewModel.alignment) { 106 | Label("Left", systemImage: "align.horizontal.left") 107 | .labelStyle(.iconOnly) 108 | .tag("Left") 109 | Label("Center", systemImage: "align.horizontal.center") 110 | .labelStyle(.iconOnly) 111 | .tag("Center") 112 | Label("Right", systemImage: "align.horizontal.right") 113 | .labelStyle(.iconOnly) 114 | .tag("Right") 115 | } 116 | .pickerStyle(.segmented) 117 | .onChange(of: textPropertiesViewModel.alignment) { newValue in 118 | self.shapes.shapeList.forEach 119 | { 120 | if $0.isSelected == true { 121 | let selectedText = $0 as! TextX 122 | selectedText.horizontalTextAlignment = newValue 123 | } 124 | } 125 | } 126 | } 127 | 128 | 129 | DimensionsPropertiesViewX(objectPropertiesViewModel: textPropertiesViewModel) 130 | 131 | 132 | } 133 | .navigationTitle("Text") 134 | //.navigationBarTitleDisplayMode(.inline) 135 | .toolbar { 136 | 137 | } 138 | } 139 | .navigationViewStyle(.stack) 140 | .frame(height:horizontalSizeClass == .regular && verticalSizeClass == .regular ? 400 : 290) 141 | .onAppear(perform: setupViewModel) 142 | .padding(.bottom, horizontalSizeClass == .regular && verticalSizeClass == .regular ? 70 : 0) 143 | 144 | } 145 | 146 | func setupViewModel() 147 | { 148 | textPropertiesViewModel.setupSelectedProperties() 149 | 150 | } 151 | 152 | } 153 | 154 | -------------------------------------------------------------------------------- /Shared/BarcodeLabelApp.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 barcoderesource.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | /* 12 | https://www.barcoderesource.com/barcodelabelappleapp.shtml 13 | 14 | https://www.barcoderesource.com/swiftui_view_vector_draw_wysiwyg.shtml 15 | */ 16 | 17 | import SwiftUI 18 | 19 | @main 20 | @available(iOS 15.0, *) 21 | struct BarcodeLabelApp: App { 22 | var body: some Scene { 23 | WindowGroup { 24 | LabelMainViewX() 25 | .environmentObject(ShapesX()) 26 | .environmentObject(AppSettings(300.0,0.28)) 27 | .environmentObject(OptionSettings("Design")) 28 | .environmentObject(PageSettings()) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Shared/IMG_20200419_102525.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/IMG_20200419_102525.jpg -------------------------------------------------------------------------------- /Shared/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Misfits-Rebels-Outcasts/SwiftUI-WYSIWYG-Draw/66d7dec9463e0fc8f74f59ed7dc950df30a9623c/Shared/test.jpg -------------------------------------------------------------------------------- /Tests iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests iOS/Tests_iOS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tests_iOS.swift 3 | // Tests iOS 4 | // 5 | // Created by on 27/01/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class Tests_iOS: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests macOS/Tests_macOS.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tests_macOS.swift 3 | // Tests macOS 4 | // 5 | // Created by on 27/01/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class Tests_macOS: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Barcode & Label 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.graphics-design 25 | LSRequiresIPhoneOS 26 | 27 | NSCameraUsageDescription 28 | This app will acquire images from camera to apply sketch or cartoon filter effects. 29 | NSPhotoLibraryAddUsageDescription 30 | This app will access your photo library to save the images with sketch or cartoon filter effects. 31 | NSPhotoLibraryUsageDescription 32 | This app will acquire images from photo library to apply sketch or cartoon filter effects. 33 | UIAppFonts 34 | 35 | ConnectCodeIND2of5_S3.otf 36 | ConnectCodeIPostnet.otf 37 | ConnectCode39_S3.otf 38 | 39 | UIApplicationSceneManifest 40 | 41 | UIApplicationSupportsMultipleScenes 42 | 43 | 44 | UIApplicationSupportsIndirectInputEvents 45 | 46 | UIFileSharingEnabled 47 | 48 | UILaunchScreen 49 | 50 | UILaunchStoryboardName 51 | BLLaunch Screen 52 | UIRequiredDeviceCapabilities 53 | 54 | still-camera 55 | armv7 56 | 57 | UISupportedInterfaceOrientations 58 | 59 | UIInterfaceOrientationPortrait 60 | 61 | UISupportedInterfaceOrientations~ipad 62 | 63 | UIInterfaceOrientationPortrait 64 | UIInterfaceOrientationPortraitUpsideDown 65 | UIInterfaceOrientationLandscapeLeft 66 | UIInterfaceOrientationLandscapeRight 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.graphics-design 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | 28 | 29 | -------------------------------------------------------------------------------- /macOS/macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.device.camera 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------