@@ -19,9 +19,21 @@ public class PdfView: UIView {
1919 @objc public var onPdfLoadComplete : PdfPageSizeHandler ?
2020 @objc public var onPdfMeasure : PdfPageSizeHandler ?
2121
22- private var annotationData = [ AnnotationPage ] ( )
22+ private let annotLayer : AnnotationView
2323 private var previousBounds : CGRect = . zero
2424
25+ public override init ( frame: CGRect ) {
26+ annotLayer = AnnotationView ( )
27+ super. init ( frame: frame)
28+ annotLayer. autoresizingMask = [ . flexibleWidth, . flexibleHeight]
29+ annotLayer. isOpaque = false
30+ addSubview ( annotLayer)
31+ }
32+
33+ required init ? ( coder: NSCoder ) {
34+ fatalError ( " init(coder:) has not been implemented " )
35+ }
36+
2537 @objc public func measurePdf( ) {
2638 guard !source. isEmpty, let dispatcher = onPdfMeasure else {
2739 return
@@ -59,16 +71,15 @@ public class PdfView: UIView {
5971 var needsMeasure = false
6072 if annotation != annot {
6173 annotation = annot
62- isDirty = true
6374 }
6475 if annotationStr != annotStr {
6576 annotationStr = annotStr
66- isDirty = true
6777 }
6878 if page != pg {
6979 page = pg
7080 isDirty = true
7181 needsMeasure = true
82+ annotLayer. setPage ( pg)
7283 }
7384 if resizeMode != rsMd {
7485 resizeMode = rsMd
@@ -91,15 +102,14 @@ public class PdfView: UIView {
91102 if bounds != previousBounds {
92103 renderPdf ( )
93104 previousBounds = bounds
105+ annotLayer. setNeedsDisplay ( )
94106 }
95107 super. layoutSubviews ( )
96108 }
97109
98110 private func loadAnnotation( file: Bool ) {
99111 guard !annotation. isEmpty || !annotationStr. isEmpty else {
100- if !annotationData. isEmpty {
101- annotationData. removeAll ( )
102- }
112+ annotLayer. setAnnotationData ( [ ] )
103113 return
104114 }
105115
@@ -111,7 +121,7 @@ public class PdfView: UIView {
111121 } else {
112122 data = annotationStr. data ( using: . utf8) !;
113123 }
114- annotationData = try decoder. decode ( [ AnnotationPage ] . self, from: data)
124+ annotLayer . setAnnotationData ( try decoder. decode ( [ AnnotationPage ] . self, from: data) )
115125 } catch {
116126 dispatchOnError (
117127 message: " Failed to load annotation from ' \( annotation) '. \( error. localizedDescription) "
@@ -120,92 +130,6 @@ public class PdfView: UIView {
120130 }
121131 }
122132
123- private func parseColor( _ hex: String ) -> UIColor {
124- // Parse HTML hex color. Assumes leading `#`.
125- guard let colorInt = UInt64 ( hex. dropFirst ( ) . prefix ( 6 ) , radix: 16 ) else {
126- return UIColor . black
127- }
128- var alpha = CGFloat ( 1.0 )
129- if hex. count == 9 , let alphaInt = UInt64 ( hex. suffix ( 2 ) , radix: 16 ) {
130- // Extract alpha channel.
131- alpha = CGFloat ( alphaInt) / 255.0
132- }
133- return UIColor (
134- red: CGFloat ( ( colorInt & 0xFF0000 ) >> 16 ) / 255.0 ,
135- green: CGFloat ( ( colorInt & 0x00FF00 ) >> 8 ) / 255.0 ,
136- blue: CGFloat ( colorInt & 0x0000FF ) / 255.0 ,
137- alpha: alpha
138- )
139- }
140-
141- private func makeCGPoint( _ point: [ CGFloat ] , _ scaleX: CGFloat , _ scaleY: CGFloat ) -> CGPoint {
142- return CGPoint ( x: scaleX * point[ 0 ] , y: scaleY * point[ 1 ] )
143- }
144-
145- private func computeDist( _ a: [ CGFloat ] , _ b: [ CGFloat ] , scaleX: CGFloat , scaleY: CGFloat ) -> CGFloat {
146- return hypot ( scaleX * ( a [ 0 ] - b[ 0 ] ) , scaleY * ( a [ 1 ] - b[ 1 ] ) )
147- }
148-
149- private func computePath( _ context: CGContext , _ coordinates: [ [ CGFloat ] ] , scaleX: CGFloat , scaleY: CGFloat ) {
150- // Start path at the first point.
151- var prevPoint = coordinates [ 0 ]
152- context. move ( to: makeCGPoint ( prevPoint, scaleX, scaleY) )
153- for point in coordinates. dropFirst ( ) {
154- guard computeDist ( prevPoint, point, scaleX: scaleX, scaleY: scaleY) > 3 else {
155- // Smooth small irregularities.
156- continue
157- }
158- let midX = ( prevPoint [ 0 ] + point[ 0 ] ) / 2
159- let midY = ( prevPoint [ 1 ] + point[ 1 ] ) / 2
160- // Draw line to the midpoint between the next two points. Use the first
161- // point as curve control (line will bend toward it).
162- context. addQuadCurve (
163- to: makeCGPoint ( [ midX, midY] , scaleX, scaleY) ,
164- control: makeCGPoint ( prevPoint, scaleX, scaleY)
165- )
166- prevPoint = point
167- }
168- // Draw line to the last point.
169- prevPoint = coordinates. last!
170- context. addLine ( to: makeCGPoint ( prevPoint, scaleX, scaleY) )
171- }
172-
173- private func renderAnnotation( _ context: CGContext , scaleX: CGFloat , scaleY: CGFloat ) {
174- guard page < annotationData. count else {
175- // No annotation data for current page.
176- return
177- }
178- let annotationPage = annotationData [ page]
179-
180- // Draw strokes.
181- context. setLineCap ( . round)
182- context. setLineJoin ( . round)
183- for stroke in annotationPage. strokes {
184- guard stroke. path. count > 1 else {
185- continue
186- }
187- context. setStrokeColor ( parseColor ( stroke. color) . cgColor)
188- context. setLineWidth ( stroke. width)
189-
190- context. beginPath ( )
191- computePath ( context, stroke. path, scaleX: scaleX, scaleY: scaleY)
192- context. strokePath ( )
193- }
194-
195- // Draw text.
196- for msg in annotationPage. text {
197- // Increase the font for larger views, but do so at a reduced rate.
198- let scaledFont = 9 + ( msg. fontSize * scaleX) / 1000
199- msg. str. draw (
200- at: makeCGPoint ( msg. point, scaleX, scaleY) ,
201- withAttributes: [
202- . font: UIFont . systemFont ( ofSize: scaledFont) ,
203- . foregroundColor: parseColor ( msg. color)
204- ]
205- )
206- }
207- }
208-
209133 private func renderPdf( ) {
210134 guard !frame. isEmpty && !source. isEmpty && readyToRender else {
211135 // View layout not yet complete, or nothing to render.
@@ -270,10 +194,6 @@ public class PdfView: UIView {
270194 context. setRenderingIntent ( . defaultIntent)
271195 context. drawPDFPage ( pdfPage)
272196 context. restoreGState ( )
273-
274- context. saveGState ( )
275- self . renderAnnotation ( context, scaleX: currentFrame. width, scaleY: currentFrame. height)
276- context. restoreGState ( )
277197 }
278198
279199 DispatchQueue . main. async {
0 commit comments