From 5d9e297f5bafdb95cd90c69abb9aec000f97d72f Mon Sep 17 00:00:00 2001 From: Brandon Withrow Date: Thu, 26 Sep 2019 14:29:49 -0500 Subject: [PATCH] Added support for stroking text --- .../CompLayers/TextCompositionLayer.swift | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/lottie-swift/src/Private/LayerContainers/CompLayers/TextCompositionLayer.swift b/lottie-swift/src/Private/LayerContainers/CompLayers/TextCompositionLayer.swift index c37e9df2a8..6ce19ea35b 100644 --- a/lottie-swift/src/Private/LayerContainers/CompLayers/TextCompositionLayer.swift +++ b/lottie-swift/src/Private/LayerContainers/CompLayers/TextCompositionLayer.swift @@ -56,6 +56,7 @@ class TextCompositionLayer: CompositionLayer { let interpolatableScale: KeyframeInterpolator? let textLayer: DisabledTextLayer = DisabledTextLayer() + let textStrokeLayer: DisabledTextLayer = DisabledTextLayer() var textProvider: AnimationTextProvider init(textLayer: TextLayerModel, textProvider: AnimationTextProvider) { @@ -65,9 +66,9 @@ class TextCompositionLayer: CompositionLayer { } self.rootNode = rootNode self.textDocument = KeyframeInterpolator(keyframes: textLayer.text.keyframes) - + self.textProvider = textProvider - + // TODO: this has to be somewhere that can be interpolated // TODO: look for inspiration from other composite layer self.interpolatableAnchorPoint = KeyframeInterpolator(keyframes: textLayer.transform.anchorPoint.keyframes) @@ -75,8 +76,9 @@ class TextCompositionLayer: CompositionLayer { super.init(layer: textLayer, size: .zero) contentsLayer.addSublayer(self.textLayer) + contentsLayer.addSublayer(self.textStrokeLayer) self.textLayer.masksToBounds = false - + self.textStrokeLayer.masksToBounds = false } required init?(coder aDecoder: NSCoder) { @@ -90,19 +92,21 @@ class TextCompositionLayer: CompositionLayer { } self.rootNode = nil self.textDocument = nil - + self.textProvider = DefaultTextProvider() - + self.interpolatableAnchorPoint = nil self.interpolatableScale = nil - + super.init(layer: layer) } - + override func displayContentsWithFrame(frame: CGFloat, forceUpdates: Bool) { guard let textDocument = textDocument else { return } textLayer.contentsScale = self.renderScale + textStrokeLayer.contentsScale = self.renderScale + let documentUpdate = textDocument.hasUpdate(frame: frame) let animatorUpdate = rootNode?.updateContents(frame, forceLocalUpdate: forceUpdates) ?? false guard documentUpdate == true || animatorUpdate == true else { return } @@ -116,31 +120,34 @@ class TextCompositionLayer: CompositionLayer { let strokeColor = rootNode?.textOutputNode.strokeColor ?? text.strokeColorData?.cgColorValue let strokeWidth = rootNode?.textOutputNode.strokeWidth ?? CGFloat(text.strokeWidth ?? 0) let tracking = (CGFloat(text.fontSize) * (rootNode?.textOutputNode.tracking ?? CGFloat(text.tracking))) / 1000.0 - // TODO LINE HEIGHT let matrix = rootNode?.textOutputNode.xform ?? CATransform3DIdentity let ctFont = CTFontCreateWithName(text.fontFamily as CFString, CGFloat(text.fontSize), nil) + let textString = textProvider.textFor(keypathName: self.keypathName, sourceText: text.text) + var attributes: [NSAttributedString.Key : Any] = [ NSAttributedString.Key.font: ctFont, NSAttributedString.Key.foregroundColor: fillColor, NSAttributedString.Key.kern: tracking, - ] - - if let strokeColor = strokeColor { - attributes[NSAttributedString.Key.strokeColor] = strokeColor - attributes[NSAttributedString.Key.strokeWidth] = strokeWidth - } - - - let textString = textProvider.textFor(keypathName: self.keypathName, sourceText: text.text) + ] let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = CGFloat(text.lineHeight) paragraphStyle.alignment = text.justification.textAlignment attributes[NSAttributedString.Key.paragraphStyle] = paragraphStyle - let attributedString = NSAttributedString(string: textString, attributes: attributes ) + let baseAttributedString = NSAttributedString(string: textString, attributes: attributes ) + + if let strokeColor = strokeColor { + textStrokeLayer.isHidden = false + attributes[NSAttributedString.Key.strokeColor] = strokeColor + attributes[NSAttributedString.Key.strokeWidth] = strokeWidth + } else { + textStrokeLayer.isHidden = true + } + + let attributedString: NSAttributedString = NSAttributedString(string: textString, attributes: attributes ) let framesetter = CTFramesetterCreateWithAttributedString(attributedString) @@ -162,15 +169,34 @@ class TextCompositionLayer: CompositionLayer { textAnchor = CGPoint(x: size.width * 0.5, y: baselinePosition) } let anchor = textAnchor + anchorPoint.pointValue - - textLayer.anchorPoint = CGPoint(x: anchor.x.remap(fromLow: 0, fromHigh: size.width, toLow: 0, toHigh: 1), - y: anchor.y.remap(fromLow: 0, fromHigh: size.height, toLow: 0, toHigh: 1)) + let normalizedAnchor = CGPoint(x: anchor.x.remap(fromLow: 0, fromHigh: size.width, toLow: 0, toHigh: 1), + y: anchor.y.remap(fromLow: 0, fromHigh: size.height, toLow: 0, toHigh: 1)) + + if textStrokeLayer.isHidden == false { + if text.strokeOverFill ?? false { + textStrokeLayer.removeFromSuperlayer() + contentsLayer.addSublayer(textStrokeLayer) + } else { + textLayer.removeFromSuperlayer() + contentsLayer.addSublayer(textLayer) + } + textStrokeLayer.anchorPoint = normalizedAnchor + textStrokeLayer.opacity = Float(rootNode?.textOutputNode.opacity ?? 1) + textStrokeLayer.transform = CATransform3DIdentity + textStrokeLayer.frame = CGRect(origin: .zero, size: size) + textStrokeLayer.position = CGPoint.zero + textStrokeLayer.transform = matrix + textStrokeLayer.string = attributedString + textStrokeLayer.alignmentMode = text.justification.caTextAlignement + } + + textLayer.anchorPoint = normalizedAnchor textLayer.opacity = Float(rootNode?.textOutputNode.opacity ?? 1) textLayer.transform = CATransform3DIdentity textLayer.frame = CGRect(origin: .zero, size: size) textLayer.position = CGPoint.zero textLayer.transform = matrix - textLayer.string = attributedString + textLayer.string = baseAttributedString textLayer.alignmentMode = text.justification.caTextAlignement } }