Overhaul silica document parsing
Also adds layer count to the info sheet
This commit is contained in:
parent
1a9e952f37
commit
d997ee38d2
6 changed files with 151 additions and 23 deletions
|
@ -24,6 +24,7 @@
|
|||
036AFC16241800350075400A /* Thumbnail.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 036AFC0B241800350075400A /* Thumbnail.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
036AFC1B241800850075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC1A241800850075400A /* ZIPFoundation */; };
|
||||
037B4042241821D200392452 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037B4041241821D200392452 /* InfoViewController.swift */; };
|
||||
03CB382424191F620078B3E5 /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 03CB382324191F620078B3E5 /* cbridge.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -79,6 +80,8 @@
|
|||
036AFC12241800350075400A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
036AFC13241800350075400A /* Thumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Thumbnail.entitlements; sourceTree = "<group>"; };
|
||||
037B4041241821D200392452 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; };
|
||||
03CB382224191F610078B3E5 /* ProcreateViewer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProcreateViewer-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
03CB382324191F620078B3E5 /* cbridge.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -144,6 +147,8 @@
|
|||
030F6FFE2415C5E400A43F01 /* ProcreateViewer.entitlements */,
|
||||
036AFBB924168C030075400A /* ViewController.swift */,
|
||||
037B4041241821D200392452 /* InfoViewController.swift */,
|
||||
03CB382324191F620078B3E5 /* cbridge.c */,
|
||||
03CB382224191F610078B3E5 /* ProcreateViewer-Bridging-Header.h */,
|
||||
);
|
||||
path = ProcreateViewer;
|
||||
sourceTree = "<group>";
|
||||
|
@ -256,6 +261,7 @@
|
|||
TargetAttributes = {
|
||||
030F6FED2415C5E300A43F01 = {
|
||||
CreatedOnToolsVersion = 11.3.1;
|
||||
LastSwiftMigration = 1130;
|
||||
};
|
||||
030F70072415C6B500A43F01 = {
|
||||
CreatedOnToolsVersion = 11.3.1;
|
||||
|
@ -321,6 +327,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
036AFBBA24168C030075400A /* ViewController.swift in Sources */,
|
||||
03CB382424191F620078B3E5 /* cbridge.c in Sources */,
|
||||
030F6FF42415C5E300A43F01 /* Document.swift in Sources */,
|
||||
037B4042241821D200392452 /* InfoViewController.swift in Sources */,
|
||||
030F6FF22415C5E300A43F01 /* AppDelegate.swift in Sources */,
|
||||
|
@ -496,6 +503,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
|
@ -507,6 +515,8 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.github.redstrate.ProcreateViewer;
|
||||
PRODUCT_NAME = "Procreate Viewer";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -516,6 +526,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
|
@ -527,6 +538,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.github.redstrate.ProcreateViewer;
|
||||
PRODUCT_NAME = "Procreate Viewer";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -149,7 +149,7 @@
|
|||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||
</connections>
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ProcreateViewer" customModuleProvider="target"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Procreate_Viewer" customModuleProvider="target"/>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<userDefaultsController representsSharedInstance="YES" id="JdS-Pg-8N9"/>
|
||||
|
@ -159,19 +159,30 @@
|
|||
<!--Info View Controller-->
|
||||
<scene sceneID="nJy-a4-E0d">
|
||||
<objects>
|
||||
<viewController showSeguePresentationStyle="single" id="wda-Mt-beD" customClass="InfoViewController" customModule="ProcreateViewer" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController showSeguePresentationStyle="single" id="wda-Mt-beD" customClass="InfoViewController" customModule="Procreate_Viewer" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="3vu-Kd-l73">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="85"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="109"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="u9u-el-oA4">
|
||||
<rect key="frame" x="18" y="49" width="444" height="16"/>
|
||||
<rect key="frame" x="18" y="73" width="444" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" placeholderString="Time Spent" id="UBd-vS-gNL">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8HM-na-o1E">
|
||||
<rect key="frame" x="18" y="49" width="444" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" id="cl0-u1-l9k">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<string key="placeholderString" base64-UTF8="YES">
|
||||
Dk51bWJlciBvZiBMYXllcnM6A
|
||||
</string>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2oe-to-Rcz">
|
||||
<rect key="frame" x="407" y="13" width="59" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="BnS-yc-3ej">
|
||||
|
@ -187,20 +198,24 @@ DQ
|
|||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="top" secondItem="u9u-el-oA4" secondAttribute="bottom" constant="8" symbolic="YES" id="BYm-qe-WxD"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="top" secondItem="3vu-Kd-l73" secondAttribute="top" constant="20" symbolic="YES" id="HaC-Tk-Y3y"/>
|
||||
<constraint firstAttribute="trailing" secondItem="u9u-el-oA4" secondAttribute="trailing" constant="20" symbolic="YES" id="Qfn-KK-5Ya"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="leading" secondItem="3vu-Kd-l73" secondAttribute="leading" constant="20" symbolic="YES" id="TpT-D9-UgA"/>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="trailing" secondItem="u9u-el-oA4" secondAttribute="trailing" id="ZND-Qt-9wv"/>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="top" secondItem="8HM-na-o1E" secondAttribute="bottom" constant="8" symbolic="YES" id="85g-Dp-ezi"/>
|
||||
<constraint firstItem="8HM-na-o1E" firstAttribute="top" secondItem="u9u-el-oA4" secondAttribute="bottom" constant="8" symbolic="YES" id="CA1-bq-4Dc"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="trailing" secondItem="8HM-na-o1E" secondAttribute="trailing" id="ILX-8S-pOI"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="top" secondItem="3vu-Kd-l73" secondAttribute="top" constant="20" symbolic="YES" id="IQj-on-T5L"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="leading" secondItem="8HM-na-o1E" secondAttribute="leading" id="KJd-X5-kxh"/>
|
||||
<constraint firstAttribute="trailing" secondItem="u9u-el-oA4" secondAttribute="trailing" constant="20" symbolic="YES" id="PlA-zS-FpK"/>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="trailing" secondItem="8HM-na-o1E" secondAttribute="trailing" id="Whu-Lh-znF"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="leading" secondItem="3vu-Kd-l73" secondAttribute="leading" constant="20" symbolic="YES" id="Xzy-ZR-1ha"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="layerCountLabel" destination="8HM-na-o1E" id="Vnq-ev-JGx"/>
|
||||
<outlet property="timeSpentLabel" destination="u9u-el-oA4" id="LeI-31-Z8r"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="Tam-yi-Bux" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="786" y="286.5"/>
|
||||
<point key="canvasLocation" x="786" y="298.5"/>
|
||||
</scene>
|
||||
<!--Window Controller-->
|
||||
<scene sceneID="R2V-B0-nI4">
|
||||
|
@ -226,7 +241,7 @@ DQ
|
|||
<!--View Controller-->
|
||||
<scene sceneID="hIz-AP-VOD">
|
||||
<objects>
|
||||
<viewController id="5gI-5U-AMq" customClass="ViewController" customModule="ProcreateViewer" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="5gI-5U-AMq" customClass="ViewController" customModule="Procreate_Viewer" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="ERx-hH-rdd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
|
|
|
@ -1,12 +1,32 @@
|
|||
import Cocoa
|
||||
import ZIPFoundation
|
||||
import CoreFoundation
|
||||
|
||||
struct DocumentInfo {
|
||||
var tracked_time: Int = 0
|
||||
struct SilicaLayer {
|
||||
|
||||
}
|
||||
|
||||
struct SilicaDocument {
|
||||
var trackedTime: Int = 0
|
||||
|
||||
var layers: [SilicaLayer] = []
|
||||
}
|
||||
|
||||
// Since this is a C-function we have to unsafe cast...
|
||||
func objectRefGetValue(_ objectRef : CFTypeRef) -> UInt32 {
|
||||
return _CFKeyedArchiverUIDGetValue(unsafeBitCast(objectRef, to: CFKeyedArchiverUIDRef.self))
|
||||
}
|
||||
|
||||
class Document: NSDocument {
|
||||
var info = DocumentInfo()
|
||||
var dict: NSDictionary?
|
||||
|
||||
let DocumentClassName = "SilicaDocument"
|
||||
let TrackedTimeKey = "SilicaDocumentTrackedTimeKey"
|
||||
let LayersKey = "layers"
|
||||
|
||||
let LayerClassName = "SilicaLayer"
|
||||
|
||||
var info = SilicaDocument()
|
||||
|
||||
var thumbnail: NSImage? = nil
|
||||
|
||||
|
@ -20,6 +40,72 @@ class Document: NSDocument {
|
|||
self.addWindowController(windowController)
|
||||
}
|
||||
|
||||
/*
|
||||
Pass in an object from the $object array, which always contains a $class key.
|
||||
*/
|
||||
func getDocumentClassName(dict: NSDictionary) -> String? {
|
||||
let objectsArray = self.dict?["$objects"] as! NSArray
|
||||
|
||||
if let value = dict["$class"] {
|
||||
let classObjectId = objectRefGetValue(value as CFTypeRef)
|
||||
let classObject = objectsArray[Int(classObjectId)] as! NSDictionary
|
||||
|
||||
return classObject["$classname"] as? String
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSilicaLayer(dict: NSDictionary) {
|
||||
if getDocumentClassName(dict: dict) == LayerClassName {
|
||||
let layer = SilicaLayer()
|
||||
// TODO: fill in layer information
|
||||
|
||||
info.layers.append(layer)
|
||||
}
|
||||
}
|
||||
|
||||
func parseSilicaDocument(dict: NSDictionary) {
|
||||
let objectsArray = self.dict?["$objects"] as! NSArray
|
||||
|
||||
if getDocumentClassName(dict: dict) == DocumentClassName {
|
||||
info.trackedTime = (dict[TrackedTimeKey] as! NSNumber).intValue
|
||||
|
||||
let layersClassKey = dict[LayersKey]
|
||||
let layersClassID = objectRefGetValue(layersClassKey as CFTypeRef)
|
||||
let layersClass = objectsArray[Int(layersClassID)] as! NSDictionary
|
||||
|
||||
let array = layersClass["NS.objects"] as! NSArray
|
||||
|
||||
for object in array {
|
||||
let layerClassID = objectRefGetValue(object as CFTypeRef)
|
||||
let layerClass = objectsArray[Int(layerClassID)] as! NSDictionary
|
||||
|
||||
parseSilicaLayer(dict: layerClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseDocument(dict: NSDictionary) {
|
||||
// double check if this archive is really correct
|
||||
if let value = dict["$version"] {
|
||||
if (value as! Int) != 100000 {
|
||||
Swift.print("This is not a valid document!")
|
||||
}
|
||||
|
||||
self.dict = dict
|
||||
|
||||
let objectsArray = dict["$objects"] as! NSArray
|
||||
|
||||
// let's read the $top class, which is always going to be SilicaDocument type.
|
||||
let topObject = dict["$top"] as! NSDictionary
|
||||
let topClassID = objectRefGetValue(topObject["root"] as CFTypeRef)
|
||||
let topObjectClass = objectsArray[Int(topClassID)] as! NSDictionary
|
||||
|
||||
parseSilicaDocument(dict: topObjectClass)
|
||||
}
|
||||
}
|
||||
|
||||
override func read(from data: Data, ofType typeName: String) throws {
|
||||
guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else {
|
||||
return
|
||||
|
@ -65,14 +151,9 @@ class Document: NSDocument {
|
|||
fatalError("failed to deserialize")
|
||||
}
|
||||
|
||||
// this is temporary, as we're just hoping that the keyed archive fits our requirements...
|
||||
let dict = (propertyList as! NSDictionary);
|
||||
|
||||
let objects = dict["$objects"] as! NSArray
|
||||
|
||||
let tracked_time = (objects[1] as! NSDictionary)["SilicaDocumentTrackedTimeKey"]
|
||||
|
||||
info.tracked_time = (tracked_time as! NSNumber).intValue
|
||||
parseDocument(dict: dict)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,16 +5,19 @@ class InfoViewController: NSViewController {
|
|||
var document: Document?
|
||||
|
||||
@IBOutlet weak var timeSpentLabel: NSTextField!
|
||||
@IBOutlet weak var layerCountLabel: NSTextField!
|
||||
|
||||
override func viewDidAppear() {
|
||||
override func viewWillAppear() {
|
||||
super.viewDidAppear()
|
||||
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.allowedUnits = [.hour, .minute, .second]
|
||||
formatter.unitsStyle = .full
|
||||
|
||||
let formattedString = formatter.string(from: TimeInterval(document!.info.tracked_time))!
|
||||
let formattedString = formatter.string(from: TimeInterval(document!.info.trackedTime))!
|
||||
|
||||
timeSpentLabel.stringValue = "Time Spent: " + formattedString
|
||||
|
||||
layerCountLabel.stringValue = "Number of layers: " + String(document!.info.layers.count)
|
||||
}
|
||||
}
|
||||
|
|
8
ProcreateViewer/ProcreateViewer-Bridging-Header.h
Normal file
8
ProcreateViewer/ProcreateViewer-Bridging-Header.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
Here we import Apple's functions to decode __CFKeyedArchiverUID types.
|
||||
*/
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
|
||||
typedef const struct __CFKeyedArchiverUID * CFKeyedArchiverUIDRef;
|
||||
|
||||
extern uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid);
|
9
ProcreateViewer/cbridge.c
Normal file
9
ProcreateViewer/cbridge.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// cbridge.c
|
||||
// ProcreateViewer
|
||||
//
|
||||
// Created by Josh on 3/11/20.
|
||||
// Copyright © 2020 Josh. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
Reference in a new issue