Overhaul parsing to better handle possible errors
This commit is contained in:
parent
8672a69e8b
commit
4c38201296
1 changed files with 120 additions and 86 deletions
|
@ -118,40 +118,51 @@ class Document: NSDocument {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? {
|
func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? {
|
||||||
let objectsArray = self.dict?["$objects"] as! NSArray
|
|
||||||
|
|
||||||
if getDocumentClassName(dict: dict) == LayerClassName {
|
if getDocumentClassName(dict: dict) == LayerClassName {
|
||||||
var layer = SilicaLayer()
|
var layer = SilicaLayer()
|
||||||
|
|
||||||
|
// TODO: when does a layer actually not have a name? i think i hit this case before
|
||||||
if let val = dict["name"] {
|
if let val = dict["name"] {
|
||||||
let NameClassID = getClassID(id: val)
|
if let name = parseBasicObject(key: val) as? String {
|
||||||
let NameClass = objectsArray[NameClassID] as! NSString
|
layer.name = name
|
||||||
|
}
|
||||||
layer.name = NameClass as String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let UUIDKey = dict["UUID"]
|
guard let uuid = parseBasicObject(key: dict["UUID"]) as? String else {
|
||||||
let UUIDClassID = getClassID(id: UUIDKey)
|
throwError(.invalid)
|
||||||
let UUIDClass = objectsArray[UUIDClassID] as! NSString
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
let maskKey = dict["mask"]
|
// sometimes mask is missing
|
||||||
let maskClassID = getClassID(id: maskKey)
|
if let maskDict = parseBasicObject(key: dict["mask"]) as? NSDictionary {
|
||||||
let maskClass = objectsArray[maskClassID]
|
layer.mask = parseSilicaLayer(archive: archive, dict: maskDict, isMask: true)?.data
|
||||||
|
}
|
||||||
|
|
||||||
layer.data.blendMode = parseRawBlendMode(blendMode: (dict["blend"] as? NSNumber)!.intValue, extendedBlend: (dict["extendedBlend"] as? NSNumber)!.intValue)!
|
guard let blendMode = (dict["blend"] as? NSNumber)?.intValue,
|
||||||
|
let extendedBlendMode = (dict["extendedBlend"] as? NSNumber)?.intValue,
|
||||||
|
let parsedBlendMode = parseRawBlendMode(blendMode: blendMode, extendedBlend: extendedBlendMode) else {
|
||||||
|
throwError(.invalid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
layer.data.opacity = (dict["opacity"] as? NSNumber)!.doubleValue
|
layer.data.blendMode = parsedBlendMode
|
||||||
layer.data.hidden = (dict["hidden"] as? Bool)!
|
|
||||||
layer.clipped = (dict["clipped"] as? Bool)!
|
|
||||||
|
|
||||||
if maskClassID != 0 {
|
if let opacity = (dict["opacity"] as? NSNumber)?.doubleValue {
|
||||||
layer.mask = parseSilicaLayer(archive: archive, dict: maskClass as! NSDictionary, isMask: true)?.data
|
layer.data.opacity = opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
if let hidden = dict["hidden"] as? Bool {
|
||||||
|
layer.data.hidden = hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
if let clipped = dict["clipped"] as? Bool {
|
||||||
|
layer.clipped = clipped
|
||||||
}
|
}
|
||||||
|
|
||||||
var chunkPaths: [String] = []
|
var chunkPaths: [String] = []
|
||||||
|
|
||||||
archive.forEach { (entry: Entry) in
|
archive.forEach { (entry: Entry) in
|
||||||
if entry.path.contains(String(UUIDClass)) {
|
if entry.path.contains(uuid) {
|
||||||
chunkPaths.append(entry.path)
|
chunkPaths.append(entry.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,28 +254,52 @@ class Document: NSDocument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBasicObject(key: Any?) -> Any? {
|
||||||
|
if let objectsArray = self.dict?["$objects"] as? NSArray {
|
||||||
|
let classID = getClassID(id: key)
|
||||||
|
|
||||||
|
return objectsArray[classID]
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseSilicaDocument(archive: Archive, dict: NSDictionary) {
|
func parseSilicaDocument(archive: Archive, dict: NSDictionary) {
|
||||||
let objectsArray = self.dict?["$objects"] as! NSArray
|
|
||||||
|
|
||||||
if getDocumentClassName(dict: dict) == DocumentClassName {
|
if getDocumentClassName(dict: dict) == DocumentClassName {
|
||||||
info.trackedTime = (dict[TrackedTimeKey] as! NSNumber).intValue
|
// failing to parse these isn't a failing case, thus we don't throw errors here.
|
||||||
info.tileSize = (dict[TileSizeKey] as! NSNumber).intValue
|
if let trackedTime = (dict[TrackedTimeKey] as? NSNumber)?.intValue {
|
||||||
info.orientation = (dict[OrientationKey] as! NSNumber).intValue
|
info.trackedTime = trackedTime
|
||||||
info.flippedHorizontally = (dict[FlippedHorizontallyKey] as! NSNumber).boolValue
|
}
|
||||||
info.flippedVertically = (dict[FlippedVerticallyKey] as! NSNumber).boolValue
|
|
||||||
|
|
||||||
let videoResolutionClassKey = dict["SilicaDocumentVideoSegmentInfoKey"]
|
if let tileSize = (dict[TileSizeKey] as? NSNumber)?.intValue {
|
||||||
let videoResolutionClassID = getClassID(id: videoResolutionClassKey)
|
info.tileSize = tileSize
|
||||||
let videoResolution = objectsArray[videoResolutionClassID] as! NSDictionary
|
}
|
||||||
|
|
||||||
let frameSizeClassKey = videoResolution["frameSize"]
|
if let orientation = (dict[OrientationKey] as? NSNumber)?.intValue {
|
||||||
let frameSizeClassID = getClassID(id: frameSizeClassKey)
|
info.orientation = orientation
|
||||||
let frameSize = objectsArray[frameSizeClassID] as! String
|
}
|
||||||
|
|
||||||
info.videoFrame = parsePairString(frameSize)!
|
if let flippedHorizontally = (dict[FlippedHorizontallyKey] as? NSNumber)?.boolValue {
|
||||||
|
info.flippedHorizontally = flippedHorizontally
|
||||||
|
}
|
||||||
|
|
||||||
let colorProfileClassKey = dict["colorProfile"]
|
if let flippedVertically = (dict[FlippedVerticallyKey] as? NSNumber)?.boolValue {
|
||||||
if colorProfileClassKey != nil {
|
info.flippedVertically = flippedVertically
|
||||||
|
}
|
||||||
|
|
||||||
|
if let videoResolution = parseBasicObject(key: dict["SilicaDocumentVideoSegmentInfoKey"]) as? NSDictionary {
|
||||||
|
guard let frameSize = parseBasicObject(key: videoResolution["frameSize"]) as? String,
|
||||||
|
let videoFrame = parsePairString(frameSize) else {
|
||||||
|
throwError(.invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info.videoFrame = videoFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if let colorProfileClassKey = dict["colorProfile"] {
|
||||||
|
let objectsArray = self.dict?["$objects"] as! NSArray
|
||||||
|
|
||||||
let colorProfileClassID = getClassID(id: colorProfileClassKey)
|
let colorProfileClassID = getClassID(id: colorProfileClassKey)
|
||||||
let colorProfile = objectsArray[colorProfileClassID] as! NSDictionary
|
let colorProfile = objectsArray[colorProfileClassID] as! NSDictionary
|
||||||
|
|
||||||
|
@ -280,52 +315,38 @@ class Document: NSDocument {
|
||||||
info.colorSpace = CGColorSpace(name: CGColorSpace.displayP3)!
|
info.colorSpace = CGColorSpace(name: CGColorSpace.displayP3)!
|
||||||
}
|
}
|
||||||
|
|
||||||
let backgroundClassKey = dict["backgroundColor"]
|
if let background = parseBasicObject(key: dict["backgroundColor"]) as? NSData {
|
||||||
let backgroundClassID = getClassID(id: backgroundClassKey)
|
var backgroundArray: [Float] = [0.0, 0.0, 0.0, 0.0]
|
||||||
let background = objectsArray[backgroundClassID] as! NSData
|
|
||||||
|
|
||||||
var backgroundArray: [Float] = [0.0, 0.0, 0.0, 0.0]
|
|
||||||
|
|
||||||
background.getBytes(&backgroundArray, length: 16)
|
|
||||||
let backgroundCgArray: [CGFloat] = [CGFloat(backgroundArray[0]), CGFloat(backgroundArray[1]), CGFloat(backgroundArray[2]), CGFloat(backgroundArray[3])]
|
|
||||||
|
|
||||||
info.backgroundColor = CGColor(colorSpace: info.colorSpace, components: backgroundCgArray)!
|
|
||||||
|
|
||||||
let strokeClassKey = dict[StrokeCountKey]
|
|
||||||
let strokeClassID = getClassID(id: strokeClassKey)
|
|
||||||
let strokeCount = objectsArray[strokeClassID] as! NSNumber
|
|
||||||
|
|
||||||
info.strokeCount = Int(truncating: strokeCount)
|
|
||||||
|
|
||||||
let nameClassKey = dict[NameKey]
|
|
||||||
let nameClassID = getClassID(id: nameClassKey)
|
|
||||||
let nameString = objectsArray[nameClassID] as! NSString
|
|
||||||
|
|
||||||
if nameString != "$null" {
|
|
||||||
info.name = nameString as String
|
|
||||||
}
|
|
||||||
|
|
||||||
let authorClassKey = dict[AuthorNameKey]
|
|
||||||
if authorClassKey != nil {
|
|
||||||
let authorClassID = getClassID(id: authorClassKey)
|
|
||||||
let authorString = objectsArray[authorClassID] as! String
|
|
||||||
|
|
||||||
if authorString != "$null" {
|
background.getBytes(&backgroundArray, length: 16)
|
||||||
info.authorName = authorString
|
let backgroundCgArray: [CGFloat] = [CGFloat(backgroundArray[0]), CGFloat(backgroundArray[1]), CGFloat(backgroundArray[2]), CGFloat(backgroundArray[3])]
|
||||||
}
|
|
||||||
|
info.backgroundColor = CGColor(colorSpace: info.colorSpace, components: backgroundCgArray)!
|
||||||
}
|
}
|
||||||
|
|
||||||
let sizeClassKey = dict[SizeKey]
|
if let strokeCount = parseBasicObject(key: dict[StrokeCountKey]) as? NSNumber {
|
||||||
let sizeClassID = getClassID(id: sizeClassKey)
|
info.strokeCount = Int(truncating: strokeCount)
|
||||||
let sizeString = objectsArray[sizeClassID] as! String
|
}
|
||||||
|
|
||||||
guard let (parsedWidth, parsedHeight) = parsePairString(sizeString) else {
|
if let name = parseBasicObject(key: dict[NameKey]) as? String,
|
||||||
|
name != "$null" {
|
||||||
|
info.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if let authorName = parseBasicObject(key: dict[AuthorNameKey]) as? String,
|
||||||
|
authorName != "$null" {
|
||||||
|
info.authorName = authorName
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let size = parseBasicObject(key: dict[SizeKey]) as? String,
|
||||||
|
let (parsedWidth, parsedHeight) = parsePairString(size) else {
|
||||||
|
throwError(.invalid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info.width = parsedWidth
|
info.width = parsedWidth
|
||||||
info.height = parsedHeight
|
info.height = parsedHeight
|
||||||
|
|
||||||
info.columns = Int(ceil(Float(info.width) / Float(info.tileSize)))
|
info.columns = Int(ceil(Float(info.width) / Float(info.tileSize)))
|
||||||
info.rows = Int(ceil(Float(info.height) / Float(info.tileSize))) + 1 // TODO: lol why
|
info.rows = Int(ceil(Float(info.height) / Float(info.tileSize))) + 1 // TODO: lol why
|
||||||
|
|
||||||
|
@ -337,17 +358,18 @@ class Document: NSDocument {
|
||||||
info.remainderHeight = (info.rows * info.tileSize) - info.height
|
info.remainderHeight = (info.rows * info.tileSize) - info.height
|
||||||
}
|
}
|
||||||
|
|
||||||
let layersClassKey = dict[LayersKey]
|
guard let layersDict = parseBasicObject(key: dict[LayersKey]) as? NSDictionary,
|
||||||
let layersClassID = getClassID(id: layersClassKey)
|
let layersArray = layersDict["NS.objects"] as? NSArray else {
|
||||||
let layersClass = objectsArray[layersClassID] as! NSDictionary
|
throwError(.invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let array = layersClass["NS.objects"] as! NSArray
|
for object in layersArray {
|
||||||
|
guard let layerDict = parseBasicObject(key: object) as? NSDictionary,
|
||||||
for object in array {
|
let layer = parseSilicaLayer(archive: archive, dict: layerDict, isMask: false) else {
|
||||||
let layerClassID = getClassID(id: object)
|
throwError(.invalid)
|
||||||
let layerClass = objectsArray[layerClassID] as! NSDictionary
|
return
|
||||||
|
}
|
||||||
guard let layer = parseSilicaLayer(archive: archive, dict: layerClass, isMask: false) else { return }
|
|
||||||
|
|
||||||
info.layers.append(layer)
|
info.layers.append(layer)
|
||||||
}
|
}
|
||||||
|
@ -364,12 +386,24 @@ class Document: NSDocument {
|
||||||
|
|
||||||
self.dict = dict
|
self.dict = dict
|
||||||
|
|
||||||
let objectsArray = dict["$objects"] as! NSArray
|
guard let objectsArray = dict["$objects"] as? NSArray else {
|
||||||
|
throwError(.invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's begin by reading $top, which is always going to be SilicaDocument type.
|
||||||
|
|
||||||
|
guard let topObject = dict["$top"] as? NSDictionary else {
|
||||||
|
throwError(.invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// let's read the $top class, which is always going to be SilicaDocument type.
|
|
||||||
let topObject = dict["$top"] as! NSDictionary
|
|
||||||
let topClassID = objectRefGetValue2(topObject["root"] as CFTypeRef)
|
let topClassID = objectRefGetValue2(topObject["root"] as CFTypeRef)
|
||||||
let topObjectClass = objectsArray[Int(topClassID)] as! NSDictionary
|
|
||||||
|
guard let topObjectClass = objectsArray[Int(topClassID)] as? NSDictionary else {
|
||||||
|
throwError(.invalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
parseSilicaDocument(archive: archive, dict: topObjectClass)
|
parseSilicaDocument(archive: archive, dict: topObjectClass)
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue