@chaitrabhairappa/react-native-rich-text-editor 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/android/src/main/java/com/richtext/editor/FloatingToolbar.kt +12 -12
  2. package/android/src/main/java/com/richtext/editor/RichTextEditorView.kt +99 -5
  3. package/android/src/main/java/com/richtext/editor/RichTextEditorViewManager.kt +18 -0
  4. package/android/src/main/java/com/richtext/editor/ToolbarIcons.kt +67 -0
  5. package/android/src/main/res/drawable/ic_format_align_center.xml +3 -3
  6. package/android/src/main/res/drawable/ic_format_align_left.xml +3 -3
  7. package/android/src/main/res/drawable/ic_format_align_right.xml +3 -3
  8. package/android/src/main/res/drawable/ic_format_bold.xml +3 -3
  9. package/android/src/main/res/drawable/ic_format_checklist.xml +3 -3
  10. package/android/src/main/res/drawable/ic_format_clear.xml +3 -3
  11. package/android/src/main/res/drawable/ic_format_code.xml +3 -3
  12. package/android/src/main/res/drawable/ic_format_heading.xml +3 -3
  13. package/android/src/main/res/drawable/ic_format_highlight.xml +3 -3
  14. package/android/src/main/res/drawable/ic_format_indent.xml +3 -3
  15. package/android/src/main/res/drawable/ic_format_italic.xml +3 -3
  16. package/android/src/main/res/drawable/ic_format_link.xml +3 -3
  17. package/android/src/main/res/drawable/ic_format_list_bulleted.xml +3 -3
  18. package/android/src/main/res/drawable/ic_format_list_numbered.xml +3 -3
  19. package/android/src/main/res/drawable/ic_format_media_attachment.xml +3 -3
  20. package/android/src/main/res/drawable/ic_format_outdent.xml +3 -3
  21. package/android/src/main/res/drawable/ic_format_quote.xml +3 -3
  22. package/android/src/main/res/drawable/ic_format_redo.xml +3 -3
  23. package/android/src/main/res/drawable/ic_format_strikethrough.xml +3 -3
  24. package/android/src/main/res/drawable/ic_format_underline.xml +3 -3
  25. package/android/src/main/res/drawable/ic_format_undo.xml +3 -3
  26. package/ios/RichTextEditorView.swift +60 -12
  27. package/ios/RichTextEditorViewManager.m +2 -0
  28. package/ios/ToolbarIcons.swift +48 -747
  29. package/lib/commonjs/RichTextEditorViewNativeComponent.js.map +1 -1
  30. package/lib/commonjs/index.js +2 -0
  31. package/lib/commonjs/index.js.map +1 -1
  32. package/lib/module/RichTextEditorViewNativeComponent.js.map +1 -1
  33. package/lib/module/index.js +2 -0
  34. package/lib/module/index.js.map +1 -1
  35. package/lib/typescript/src/RichTextEditorViewNativeComponent.d.ts +2 -0
  36. package/lib/typescript/src/RichTextEditorViewNativeComponent.d.ts.map +1 -1
  37. package/lib/typescript/src/index.d.ts.map +1 -1
  38. package/lib/typescript/src/types.d.ts +2 -0
  39. package/lib/typescript/src/types.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/RichTextEditorViewNativeComponent.ts +2 -0
  42. package/src/index.tsx +2 -0
  43. package/src/types.ts +2 -0
@@ -1,760 +1,61 @@
1
1
  import UIKit
2
2
 
3
- /// Provides vector icons for the rich text editor toolbar
4
- /// Icons match the Android VectorDrawable implementations for cross-platform consistency
3
+ /// Provides toolbar icons as pre-rendered PNG images (base64-encoded).
4
+ /// Icons are identical to those used on Android for cross-platform consistency.
5
+ /// All icons are rendered from the same SVG path data at 72x72px (3x density).
5
6
  class ToolbarIcons {
6
7
 
7
- static func getIcon(for option: String, color: UIColor, size: CGSize = CGSize(width: 24, height: 24)) -> UIImage? {
8
- switch option {
9
- case "bold": return drawBoldIcon(color: color, size: size)
10
- case "italic": return drawItalicIcon(color: color, size: size)
11
- case "underline": return drawUnderlineIcon(color: color, size: size)
12
- case "strikethrough": return drawStrikethroughIcon(color: color, size: size)
13
- case "code": return drawCodeIcon(color: color, size: size)
14
- case "highlight": return drawHighlightIcon(color: color, size: size)
15
- case "heading": return drawHeadingIcon(color: color, size: size)
16
- case "bullet": return drawBulletListIcon(color: color, size: size)
17
- case "numbered": return drawNumberedListIcon(color: color, size: size)
18
- case "quote": return drawQuoteIcon(color: color, size: size)
19
- case "checklist": return drawChecklistIcon(color: color, size: size)
20
- case "mediaAttachment": return drawMediaAttachmentIcon(color: color, size: size)
21
- case "link": return drawLinkIcon(color: color, size: size)
22
- case "undo": return drawUndoIcon(color: color, size: size)
23
- case "redo": return drawRedoIcon(color: color, size: size)
24
- case "clearFormatting": return drawClearFormattingIcon(color: color, size: size)
25
- case "indent": return drawIndentIcon(color: color, size: size)
26
- case "outdent": return drawOutdentIcon(color: color, size: size)
27
- case "alignLeft": return drawAlignLeftIcon(color: color, size: size)
28
- case "alignCenter": return drawAlignCenterIcon(color: color, size: size)
29
- case "alignRight": return drawAlignRightIcon(color: color, size: size)
30
- default: return nil
31
- }
32
- }
33
-
34
- // MARK: - Icon Drawing Methods
35
-
36
- private static func drawBoldIcon(color: UIColor, size: CGSize) -> UIImage {
37
- return drawIcon(size: size, color: color) { ctx, scale in
38
- let path = UIBezierPath()
39
- path.move(to: CGPoint(x: 15.6 * scale, y: 10.79 * scale))
40
- path.addCurve(to: CGPoint(x: 17.25 * scale, y: 8 * scale),
41
- controlPoint1: CGPoint(x: 16.57 * scale, y: 10.12 * scale),
42
- controlPoint2: CGPoint(x: 17.25 * scale, y: 9.02 * scale))
43
- path.addCurve(to: CGPoint(x: 13.25 * scale, y: 4 * scale),
44
- controlPoint1: CGPoint(x: 17.25 * scale, y: 5.74 * scale),
45
- controlPoint2: CGPoint(x: 15.5 * scale, y: 4 * scale))
46
- path.addLine(to: CGPoint(x: 7 * scale, y: 4 * scale))
47
- path.addLine(to: CGPoint(x: 7 * scale, y: 18 * scale))
48
- path.addLine(to: CGPoint(x: 14.04 * scale, y: 18 * scale))
49
- path.addCurve(to: CGPoint(x: 17.75 * scale, y: 14.21 * scale),
50
- controlPoint1: CGPoint(x: 16.13 * scale, y: 18 * scale),
51
- controlPoint2: CGPoint(x: 17.75 * scale, y: 16.51 * scale))
52
- path.addCurve(to: CGPoint(x: 15.6 * scale, y: 10.79 * scale),
53
- controlPoint1: CGPoint(x: 17.75 * scale, y: 12.69 * scale),
54
- controlPoint2: CGPoint(x: 16.89 * scale, y: 11.39 * scale))
55
- path.close()
56
-
57
- path.move(to: CGPoint(x: 10 * scale, y: 6.5 * scale))
58
- path.addLine(to: CGPoint(x: 13 * scale, y: 6.5 * scale))
59
- path.addCurve(to: CGPoint(x: 14.5 * scale, y: 8 * scale),
60
- controlPoint1: CGPoint(x: 13.83 * scale, y: 6.5 * scale),
61
- controlPoint2: CGPoint(x: 14.5 * scale, y: 7.17 * scale))
62
- path.addCurve(to: CGPoint(x: 13 * scale, y: 9.5 * scale),
63
- controlPoint1: CGPoint(x: 14.5 * scale, y: 8.83 * scale),
64
- controlPoint2: CGPoint(x: 13.83 * scale, y: 9.5 * scale))
65
- path.addLine(to: CGPoint(x: 10 * scale, y: 9.5 * scale))
66
- path.addLine(to: CGPoint(x: 10 * scale, y: 6.5 * scale))
67
- path.close()
68
-
69
- path.move(to: CGPoint(x: 13.5 * scale, y: 15.5 * scale))
70
- path.addLine(to: CGPoint(x: 10 * scale, y: 15.5 * scale))
71
- path.addLine(to: CGPoint(x: 10 * scale, y: 12.5 * scale))
72
- path.addLine(to: CGPoint(x: 13.5 * scale, y: 12.5 * scale))
73
- path.addCurve(to: CGPoint(x: 15 * scale, y: 14 * scale),
74
- controlPoint1: CGPoint(x: 14.33 * scale, y: 12.5 * scale),
75
- controlPoint2: CGPoint(x: 15 * scale, y: 13.17 * scale))
76
- path.addCurve(to: CGPoint(x: 13.5 * scale, y: 15.5 * scale),
77
- controlPoint1: CGPoint(x: 15 * scale, y: 14.83 * scale),
78
- controlPoint2: CGPoint(x: 14.33 * scale, y: 15.5 * scale))
79
- path.close()
80
-
81
- path.fill()
82
- }
83
- }
84
-
85
- private static func drawItalicIcon(color: UIColor, size: CGSize) -> UIImage {
86
- return drawIcon(size: size, color: color) { ctx, scale in
87
- let path = UIBezierPath()
88
- path.move(to: CGPoint(x: 10 * scale, y: 4 * scale))
89
- path.addLine(to: CGPoint(x: 10 * scale, y: 7 * scale))
90
- path.addLine(to: CGPoint(x: 12.21 * scale, y: 7 * scale))
91
- path.addLine(to: CGPoint(x: 8.79 * scale, y: 15 * scale))
92
- path.addLine(to: CGPoint(x: 6 * scale, y: 15 * scale))
93
- path.addLine(to: CGPoint(x: 6 * scale, y: 18 * scale))
94
- path.addLine(to: CGPoint(x: 14 * scale, y: 18 * scale))
95
- path.addLine(to: CGPoint(x: 14 * scale, y: 15 * scale))
96
- path.addLine(to: CGPoint(x: 11.79 * scale, y: 15 * scale))
97
- path.addLine(to: CGPoint(x: 15.21 * scale, y: 7 * scale))
98
- path.addLine(to: CGPoint(x: 18 * scale, y: 7 * scale))
99
- path.addLine(to: CGPoint(x: 18 * scale, y: 4 * scale))
100
- path.close()
101
- path.fill()
102
- }
103
- }
104
-
105
- private static func drawUnderlineIcon(color: UIColor, size: CGSize) -> UIImage {
106
- return drawIcon(size: size, color: color) { ctx, scale in
107
- let path = UIBezierPath()
108
- // U shape
109
- path.move(to: CGPoint(x: 12 * scale, y: 17 * scale))
110
- path.addCurve(to: CGPoint(x: 18 * scale, y: 11 * scale),
111
- controlPoint1: CGPoint(x: 15.31 * scale, y: 17 * scale),
112
- controlPoint2: CGPoint(x: 18 * scale, y: 14.31 * scale))
113
- path.addLine(to: CGPoint(x: 18 * scale, y: 3 * scale))
114
- path.addLine(to: CGPoint(x: 15.5 * scale, y: 3 * scale))
115
- path.addLine(to: CGPoint(x: 15.5 * scale, y: 11 * scale))
116
- path.addCurve(to: CGPoint(x: 12 * scale, y: 14.5 * scale),
117
- controlPoint1: CGPoint(x: 15.5 * scale, y: 12.93 * scale),
118
- controlPoint2: CGPoint(x: 13.93 * scale, y: 14.5 * scale))
119
- path.addCurve(to: CGPoint(x: 8.5 * scale, y: 11 * scale),
120
- controlPoint1: CGPoint(x: 10.07 * scale, y: 14.5 * scale),
121
- controlPoint2: CGPoint(x: 8.5 * scale, y: 12.93 * scale))
122
- path.addLine(to: CGPoint(x: 8.5 * scale, y: 3 * scale))
123
- path.addLine(to: CGPoint(x: 6 * scale, y: 3 * scale))
124
- path.addLine(to: CGPoint(x: 6 * scale, y: 11 * scale))
125
- path.addCurve(to: CGPoint(x: 12 * scale, y: 17 * scale),
126
- controlPoint1: CGPoint(x: 6 * scale, y: 14.31 * scale),
127
- controlPoint2: CGPoint(x: 8.69 * scale, y: 17 * scale))
128
- path.close()
129
-
130
- // Underline
131
- path.move(to: CGPoint(x: 5 * scale, y: 19 * scale))
132
- path.addLine(to: CGPoint(x: 5 * scale, y: 21 * scale))
133
- path.addLine(to: CGPoint(x: 19 * scale, y: 21 * scale))
134
- path.addLine(to: CGPoint(x: 19 * scale, y: 19 * scale))
135
- path.close()
136
-
137
- path.fill()
138
- }
139
- }
140
-
141
- private static func drawStrikethroughIcon(color: UIColor, size: CGSize) -> UIImage {
142
- return drawIcon(size: size, color: color) { ctx, scale in
143
- let path = UIBezierPath()
144
- // Top part
145
- path.move(to: CGPoint(x: 10 * scale, y: 19 * scale))
146
- path.addLine(to: CGPoint(x: 14 * scale, y: 19 * scale))
147
- path.addLine(to: CGPoint(x: 14 * scale, y: 16 * scale))
148
- path.addLine(to: CGPoint(x: 10 * scale, y: 16 * scale))
149
- path.close()
150
-
151
- // S shape top
152
- path.move(to: CGPoint(x: 5 * scale, y: 4 * scale))
153
- path.addLine(to: CGPoint(x: 5 * scale, y: 7 * scale))
154
- path.addLine(to: CGPoint(x: 10 * scale, y: 7 * scale))
155
- path.addLine(to: CGPoint(x: 10 * scale, y: 10 * scale))
156
- path.addLine(to: CGPoint(x: 14 * scale, y: 10 * scale))
157
- path.addLine(to: CGPoint(x: 14 * scale, y: 7 * scale))
158
- path.addLine(to: CGPoint(x: 19 * scale, y: 7 * scale))
159
- path.addLine(to: CGPoint(x: 19 * scale, y: 4 * scale))
160
- path.close()
161
-
162
- // Strike line
163
- path.move(to: CGPoint(x: 3 * scale, y: 14 * scale))
164
- path.addLine(to: CGPoint(x: 21 * scale, y: 14 * scale))
165
- path.addLine(to: CGPoint(x: 21 * scale, y: 12 * scale))
166
- path.addLine(to: CGPoint(x: 3 * scale, y: 12 * scale))
167
- path.close()
168
-
169
- path.fill()
170
- }
171
- }
172
-
173
- private static func drawCodeIcon(color: UIColor, size: CGSize) -> UIImage {
174
- return drawIcon(size: size, color: color) { ctx, scale in
175
- let path = UIBezierPath()
176
- // Left bracket <
177
- path.move(to: CGPoint(x: 9.4 * scale, y: 16.6 * scale))
178
- path.addLine(to: CGPoint(x: 4.8 * scale, y: 12 * scale))
179
- path.addLine(to: CGPoint(x: 9.4 * scale, y: 7.4 * scale))
180
- path.addLine(to: CGPoint(x: 8 * scale, y: 6 * scale))
181
- path.addLine(to: CGPoint(x: 2 * scale, y: 12 * scale))
182
- path.addLine(to: CGPoint(x: 8 * scale, y: 18 * scale))
183
- path.close()
184
-
185
- // Right bracket >
186
- path.move(to: CGPoint(x: 14.6 * scale, y: 16.6 * scale))
187
- path.addLine(to: CGPoint(x: 19.2 * scale, y: 12 * scale))
188
- path.addLine(to: CGPoint(x: 14.6 * scale, y: 7.4 * scale))
189
- path.addLine(to: CGPoint(x: 16 * scale, y: 6 * scale))
190
- path.addLine(to: CGPoint(x: 22 * scale, y: 12 * scale))
191
- path.addLine(to: CGPoint(x: 16 * scale, y: 18 * scale))
192
- path.close()
193
-
194
- path.fill()
195
- }
196
- }
197
-
198
- private static func drawHighlightIcon(color: UIColor, size: CGSize) -> UIImage {
199
- return drawIcon(size: size, color: color) { ctx, scale in
200
- let path = UIBezierPath()
201
- // Marker body
202
- path.move(to: CGPoint(x: 6 * scale, y: 14 * scale))
203
- path.addLine(to: CGPoint(x: 9 * scale, y: 17 * scale))
204
- path.addLine(to: CGPoint(x: 9 * scale, y: 22 * scale))
205
- path.addLine(to: CGPoint(x: 15 * scale, y: 22 * scale))
206
- path.addLine(to: CGPoint(x: 15 * scale, y: 17 * scale))
207
- path.addLine(to: CGPoint(x: 18 * scale, y: 14 * scale))
208
- path.addLine(to: CGPoint(x: 18 * scale, y: 9 * scale))
209
- path.addLine(to: CGPoint(x: 6 * scale, y: 9 * scale))
210
- path.close()
211
-
212
- // Top line
213
- path.move(to: CGPoint(x: 11 * scale, y: 2 * scale))
214
- path.addLine(to: CGPoint(x: 13 * scale, y: 2 * scale))
215
- path.addLine(to: CGPoint(x: 13 * scale, y: 5 * scale))
216
- path.addLine(to: CGPoint(x: 11 * scale, y: 5 * scale))
217
- path.close()
218
-
219
- // Left ray
220
- path.move(to: CGPoint(x: 3.5 * scale, y: 5.88 * scale))
221
- path.addLine(to: CGPoint(x: 4.91 * scale, y: 4.47 * scale))
222
- path.addLine(to: CGPoint(x: 7.03 * scale, y: 6.59 * scale))
223
- path.addLine(to: CGPoint(x: 5.62 * scale, y: 8 * scale))
224
- path.close()
225
-
226
- // Right ray
227
- path.move(to: CGPoint(x: 16.96 * scale, y: 6.59 * scale))
228
- path.addLine(to: CGPoint(x: 19.08 * scale, y: 4.47 * scale))
229
- path.addLine(to: CGPoint(x: 20.49 * scale, y: 5.88 * scale))
230
- path.addLine(to: CGPoint(x: 18.37 * scale, y: 8 * scale))
231
- path.close()
232
-
233
- path.fill()
234
- }
235
- }
236
-
237
- private static func drawHeadingIcon(color: UIColor, size: CGSize) -> UIImage {
238
- return drawIcon(size: size, color: color) { ctx, scale in
239
- let path = UIBezierPath()
240
- path.move(to: CGPoint(x: 5 * scale, y: 4 * scale))
241
- path.addLine(to: CGPoint(x: 5 * scale, y: 7 * scale))
242
- path.addLine(to: CGPoint(x: 10.5 * scale, y: 7 * scale))
243
- path.addLine(to: CGPoint(x: 10.5 * scale, y: 19 * scale))
244
- path.addLine(to: CGPoint(x: 13.5 * scale, y: 19 * scale))
245
- path.addLine(to: CGPoint(x: 13.5 * scale, y: 7 * scale))
246
- path.addLine(to: CGPoint(x: 19 * scale, y: 7 * scale))
247
- path.addLine(to: CGPoint(x: 19 * scale, y: 4 * scale))
248
- path.close()
249
- path.fill()
250
- }
251
- }
252
-
253
- private static func drawBulletListIcon(color: UIColor, size: CGSize) -> UIImage {
254
- return drawIcon(size: size, color: color) { ctx, scale in
255
- let path = UIBezierPath()
256
- // Bullets
257
- path.append(UIBezierPath(ovalIn: CGRect(x: 2.5 * scale, y: 10.5 * scale, width: 3 * scale, height: 3 * scale)))
258
- path.append(UIBezierPath(ovalIn: CGRect(x: 2.5 * scale, y: 4.5 * scale, width: 3 * scale, height: 3 * scale)))
259
- path.append(UIBezierPath(ovalIn: CGRect(x: 2.5 * scale, y: 16.5 * scale, width: 3 * scale, height: 3 * scale)))
260
-
261
- // Lines
262
- path.move(to: CGPoint(x: 7 * scale, y: 19 * scale))
263
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
264
- path.addLine(to: CGPoint(x: 21 * scale, y: 17 * scale))
265
- path.addLine(to: CGPoint(x: 7 * scale, y: 17 * scale))
266
- path.close()
267
-
268
- path.move(to: CGPoint(x: 7 * scale, y: 13 * scale))
269
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
270
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
271
- path.addLine(to: CGPoint(x: 7 * scale, y: 11 * scale))
272
- path.close()
273
-
274
- path.move(to: CGPoint(x: 7 * scale, y: 5 * scale))
275
- path.addLine(to: CGPoint(x: 7 * scale, y: 7 * scale))
276
- path.addLine(to: CGPoint(x: 21 * scale, y: 7 * scale))
277
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
278
- path.close()
279
-
280
- path.fill()
281
- }
282
- }
283
-
284
- private static func drawNumberedListIcon(color: UIColor, size: CGSize) -> UIImage {
285
- return drawIcon(size: size, color: color) { ctx, scale in
286
- // Draw numbers using text
287
- let paragraphStyle = NSMutableParagraphStyle()
288
- paragraphStyle.alignment = .left
289
- let attrs: [NSAttributedString.Key: Any] = [
290
- .font: UIFont.systemFont(ofSize: 8 * scale, weight: .medium),
291
- .foregroundColor: color
292
- ]
293
-
294
- "1".draw(at: CGPoint(x: 3 * scale, y: 4 * scale), withAttributes: attrs)
295
- "2".draw(at: CGPoint(x: 3 * scale, y: 10 * scale), withAttributes: attrs)
296
- "3".draw(at: CGPoint(x: 3 * scale, y: 16 * scale), withAttributes: attrs)
297
-
298
- // Lines
299
- let path = UIBezierPath()
300
- path.move(to: CGPoint(x: 7 * scale, y: 5 * scale))
301
- path.addLine(to: CGPoint(x: 7 * scale, y: 7 * scale))
302
- path.addLine(to: CGPoint(x: 21 * scale, y: 7 * scale))
303
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
304
- path.close()
305
-
306
- path.move(to: CGPoint(x: 7 * scale, y: 13 * scale))
307
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
308
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
309
- path.addLine(to: CGPoint(x: 7 * scale, y: 11 * scale))
310
- path.close()
311
-
312
- path.move(to: CGPoint(x: 7 * scale, y: 19 * scale))
313
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
314
- path.addLine(to: CGPoint(x: 21 * scale, y: 17 * scale))
315
- path.addLine(to: CGPoint(x: 7 * scale, y: 17 * scale))
316
- path.close()
317
-
318
- path.fill()
319
- }
320
- }
321
-
322
- private static func drawQuoteIcon(color: UIColor, size: CGSize) -> UIImage {
323
- return drawIcon(size: size, color: color) { ctx, scale in
324
- let path = UIBezierPath()
325
- // Left quote
326
- path.move(to: CGPoint(x: 6 * scale, y: 17 * scale))
327
- path.addLine(to: CGPoint(x: 9 * scale, y: 17 * scale))
328
- path.addLine(to: CGPoint(x: 11 * scale, y: 13 * scale))
329
- path.addLine(to: CGPoint(x: 11 * scale, y: 7 * scale))
330
- path.addLine(to: CGPoint(x: 5 * scale, y: 7 * scale))
331
- path.addLine(to: CGPoint(x: 5 * scale, y: 13 * scale))
332
- path.addLine(to: CGPoint(x: 8 * scale, y: 13 * scale))
333
- path.close()
334
-
335
- // Right quote
336
- path.move(to: CGPoint(x: 14 * scale, y: 17 * scale))
337
- path.addLine(to: CGPoint(x: 17 * scale, y: 17 * scale))
338
- path.addLine(to: CGPoint(x: 19 * scale, y: 13 * scale))
339
- path.addLine(to: CGPoint(x: 19 * scale, y: 7 * scale))
340
- path.addLine(to: CGPoint(x: 13 * scale, y: 7 * scale))
341
- path.addLine(to: CGPoint(x: 13 * scale, y: 13 * scale))
342
- path.addLine(to: CGPoint(x: 16 * scale, y: 13 * scale))
343
- path.close()
344
-
345
- path.fill()
346
- }
347
- }
348
-
349
- private static func drawChecklistIcon(color: UIColor, size: CGSize) -> UIImage {
350
- return drawIcon(size: size, color: color) { ctx, scale in
351
- let path = UIBezierPath()
352
- // Lines
353
- path.move(to: CGPoint(x: 22 * scale, y: 7 * scale))
354
- path.addLine(to: CGPoint(x: 13 * scale, y: 7 * scale))
355
- path.addLine(to: CGPoint(x: 13 * scale, y: 9 * scale))
356
- path.addLine(to: CGPoint(x: 22 * scale, y: 9 * scale))
357
- path.close()
358
-
359
- path.move(to: CGPoint(x: 22 * scale, y: 15 * scale))
360
- path.addLine(to: CGPoint(x: 13 * scale, y: 15 * scale))
361
- path.addLine(to: CGPoint(x: 13 * scale, y: 17 * scale))
362
- path.addLine(to: CGPoint(x: 22 * scale, y: 17 * scale))
363
- path.close()
364
-
365
- // Checkmarks
366
- path.move(to: CGPoint(x: 5.54 * scale, y: 11 * scale))
367
- path.addLine(to: CGPoint(x: 2 * scale, y: 7.46 * scale))
368
- path.addLine(to: CGPoint(x: 3.41 * scale, y: 6.05 * scale))
369
- path.addLine(to: CGPoint(x: 5.53 * scale, y: 8.17 * scale))
370
- path.addLine(to: CGPoint(x: 9.77 * scale, y: 3.93 * scale))
371
- path.addLine(to: CGPoint(x: 11.18 * scale, y: 5.34 * scale))
372
- path.close()
373
-
374
- path.move(to: CGPoint(x: 5.54 * scale, y: 19 * scale))
375
- path.addLine(to: CGPoint(x: 2 * scale, y: 15.46 * scale))
376
- path.addLine(to: CGPoint(x: 3.41 * scale, y: 14.05 * scale))
377
- path.addLine(to: CGPoint(x: 5.53 * scale, y: 16.17 * scale))
378
- path.addLine(to: CGPoint(x: 9.77 * scale, y: 11.93 * scale))
379
- path.addLine(to: CGPoint(x: 11.18 * scale, y: 13.34 * scale))
380
- path.close()
381
-
382
- path.fill()
383
- }
384
- }
8
+ private static let iconData: [String: String] = [
9
+ "bold": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABvElEQVR4nO3aP0uXURjG8SvKpSgVaukl1BI1CM4u2Z/XUGtvQNwcBdFotEFEaCjaamhp7R+0REttFTRJZYMpBt845OL03IWd87s71wfOeg0XD4fznHNLZmZmZmZm9l/gcG0Cb4GHwA3gtLLj39oFVoEzyoo6PgNTyoh6fgDXlQ11bQOXlAn1vQfGlAVt3FIWxCwG1grwPJj3ETiiDAj4w7xZ4Gsg9qJ6LKgArgRi59VrQQXwYiD2rjovaHkg9pE6L2hlIPaxOi/o5UDsHfVaEHA1EHtNPRbE73K+DUR+SnOaJmYusG4Dr4J5N5UF9d1XJtT1BjipTKjnKTChbKhjB5hRRtS1BhxXJtRXDpDjyoI2yn50VBnQznxvN4qL+2sD+BLI/J7ivYyAv8gcBx4EoufUY0EFcAx4NhD9Wr0WVACXB6L3gBPquKBTgfjzGmW0L2haHRc0G4g/px4LAsaCD4mT6q0gYHJ/iGrIO406Du9GsawF4F7wZbVY1qijnZ8jv/80LmhdGdDGhxT/YUWDcrZSTZlRV3kPu6BMqHcnveRL+4PDmmUW8UkZtwPOtv4QzMzMzMzMzMzMzHTALzu/TlkIFz7bAAAAAElFTkSuQmCC",
10
+ "italic": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABIElEQVR4nO3Xy0mDURRF4a1jjU34qkRCsBEJIdiIhBBsREKwEqM24WOsS2zgPwchg7j3N72zxYHNlSIiIiIiIsIGO6Z9RwINS6BCAhUSqJBAhQQqJFAhgf4IuG/Eu5EjYAS8F3F+30dyBNw2rudOjoAD4LmI8w2cyxFw3bieB7kCHhuBruQIOAO+ijgvwKEcAavG9UzlCDhuTPuH87TPG9ezkPG0bxvTfiFHwKRxPWu5AjaNQGM5ItM+DFg2rmcm42l/a0z7iRwBs8b1LGU87U+Nab+UI2DcuJ6NXAHrRqCJHAGnjV/7q/OvfdG4nrkcAUeNaf90nvZp43pW+q/YMe07EmhYAhUSqJBAhQQqJFAhgQoJFBEREREREREREbL3A+hWfBELhFT9AAAAAElFTkSuQmCC",
11
+ "underline": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxElEQVR4nO3XQUpbQRzH8T910era7ltBe41SeoWiJ6h4Cw+gySYU92nTVXuIiIQWcSNVz6AeIG7ylYG3kFLm91Ln4Tz5fSAQQpjf5Jf35s1EmJmZmZl1ibz9DvL2c4FRG1xQngsSXJDgggQXJLggwQUJLkhwQYILElyQ4IIEFyS4IMEFCS5IcEGCCxJckOCCBBckuCDBBQnAXaakgygMGGTy5lEb4DYz4UkHed8zeddRG+AsM+E/HeRdZvJOozbAN/I2C2a9E1njqA2wJyZ9WDBrKLI+R22ADWCRmfQceFMoJ/dAWJTI6QRwIv7ZU2DtEeO/An6JjGnUCthB+5F+6H+MvQr8bDH+p6gVsAJctPgR6Sp4u+Rt9bvFuOfAi6gZ8JF25s1iuyWeVkOx5jxce95HHwBHLCftaSbN7njQvL9acoxR9AXtFtOSZsDL6BPgdcv16LFSxnr0EbDe8ZU06205f91uXwoXkxbkUe9uqxzgQzq0FijnvDdPq2WlPQqwDRyLY8m/rphp2gRWv88pJW0UgV3ga3MEuW32O+l103w2br5T59nKzMzs+aInXJDgggQXJLggwQUJT/soMzMzM7N4ju4BPDGZUVsSfzcAAAAASUVORK5CYII=",
12
+ "strikethrough": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAdUlEQVR4nO3VwQ2AQAzAsO6/dNmAiBc6sEfIJzMAAAAcZQ8hUBAoCBQECgIFgYJAQaAwJ9qH5m9WoHsCBYGCQEGgIFAQKAgEAEcu+RQCBYGCQEGgIFB4d30A8Fn70PzNCnRPoCBQECgIFAQKAgWBAAAAAOYNF59/SnvOM5yQAAAAAElFTkSuQmCC",
13
+ "code": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABGklEQVR4nO3ZMU7DQBgF4a3TIYHETVPapQ9HQ8N1BkWQIlKUAPHu/k/Md4Dn3ZGVIm5NkiRJkiRJkkoAVmBL3e+Kr8OfbWn7XXF5+N0v0Xu/K64ffrdL9N7vituHP3kHDg/sH743bllbcJznHZ7zBLzdedb2L+NERmJwnKhITIoTEYnJcUpHokickpEoFqdUJIrGKRGJ4nGmRiIkzpRIhMUZGonQOEMipcfpHgk43hn9AF5bccDLD/4BWP46fkyO1DVOeiRGxEmNNDROWqQpcVIiTY1TPVKJOFUjlYpTLVLJOFUilY4zO1JEnFmRouKMjhQZ58xPz4+/SUv1/SG4foklZX8ILi+xpO2P/E1aU/clSZIkSZIkqf3KJ7XdjUR0XLs+AAAAAElFTkSuQmCC",
14
+ "highlight": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeUlEQVR4nO3UTUrEQBRF4Wfj1J/dOHMuuAAHoiC4BoeuQxHRrYiCuCWVIw0GGjX1ukkquYn3W0DVy6HyIszMzKwDWnQ5c1ZwoDIHSjhQwoESDvQfAwHHwI56IGAfOOrjrE0uPQE+gBdgVzUQsAe8fs962vW8TeM0eonUt5U4jfqR+B1HMtIfcepHAg6BT9o997WTetg5b4U534GDGhcvgIfCxaO/pMLLWXW3/JZaAyyAx2SAp2oDlGfbXiPOLbBVe5BF4SUtf8HzqgOUZ7sorIF6L2fNSKPGSSINF6clkkSclkjDx/kR6R44CzHAJXBTfeeYmQTEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrEhBrgCh3XoQiNSJpxRCJpxxk50jTijBRpWnEGjjTNOANFmnacypHmEadSpHnFafRVJ+YKBypzoIQDJRwo4UAJB0o4UMKBEg6UcKCEAyUcKOFACQcyMzOLAX0B6ECHyn/I8bMAAAAASUVORK5CYII=",
15
+ "heading": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhUlEQVR4nO3QsQ2AQBADwSudzg/RAJshvZiJnXhnAAAAOMoeQqAgUBAoCBQECgIFgYJAYU61u1edezbzVyvQO4GCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQAAAAADztRu2Ullm5rngbwAAAABJRU5ErkJggg==",
16
+ "bullet": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABG0lEQVR4nO3aXW7CMBBFYa8AWAwEuqsSFoYotCzvVJbSpzixpUYKxueT8hSRsSbk72pCkCRJkiSpVsAH0ANn4LT2el4GsAOejH3HfaF1pJvz5zHxmyosdVnlHFtu0KWgTm+D5p1bbtCpoE7XbIOi4Wk15R4SqERY8DH/SBz/DmwXKfIOgG54SfwEDmuvR5IkSS8CE8XJxuxMFOf/Oc+Z7z0TRfJMFDNMFDNMFDNMFJlmooiJYvGLYmeiKEmSpBFMFNOALfCT+A6LAw0OLzA//vI10dQq/PtGgQNU2QZdCk5E04liX1Cn6UTxWFBnNExFKw2K4o14psY1JFCJsARgA9wSx7/GfYsUeQfAYZhPjNt+7fVIkiRJkhRa9QtNmk8JUv9uigAAAABJRU5ErkJggg==",
17
+ "numbered": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABDklEQVR4nO3XsU3EMBhAYReIDokNKFiHillArMAEVCxCxQwUzEBBfR3o0URgieRsnxPEf36flC7+o7wkSpKSJEmSJOnfYEGSgap4BzWg8REjiJYGBupBY3WC6IpioAY0VieIlgYG6sEW1Y8JBjKQJEkD+dNP9YgM1B9oV5oxFOACeJvifALXe/YNYc04Z8BLNvumsH8Ia8U5AZ6yuY8Va4YK9JDNfAZODfQT5zaL8wqcV0YNoffOuQI+plnvwGXD2iEC7TY9QHSbX4HoDCRJ0tHyNV9goAIDHYiKXw2COLTB3AnfZVvxAAwYaJaBJvN5DPQNuM82A+2DgX6HWJJGtlglk0ZGhSRJkiRJUlrfF/qJCMx4HPSsAAAAAElFTkSuQmCC",
18
+ "quote": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAsklEQVR4nO3S0QkCURAEwQvNzA1tzOCdHzYqVAXQ7MBeFwAAAAAA8P/2Id/q5xYPqPu5xQPqfm7xgLqfWzyg7ucWD6j7ucUD6n5u8YC6n1s8oO7nFg+o+7nFA+p+bvGAup9bPKDu5xYPqPu5xQPqfm7xgLqfWzyg7ucWD6j7X7ft8cb9z1/t57Y93xjw+NV+ar7nzPcc+J4bvufA99zwPQe+54bvOfA9N3wPAAAAAABwlV6W3Lr8+te/TgAAAABJRU5ErkJggg==",
19
+ "checklist": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVR4nO3ZzUkDQQBH8fHsJdiCJ78gQUsSLMUCPGktYgEe7EEvagixAn2ymIUo7MxkNya7mfc7m3XymITAPwRJkiRJ2g3A/rbP0FvAJfAMHG77LH2N88mPFyM1x6kZKRLHSBlxKm/AcSgR6TjvwEkYOmACXAN7K7zmCviKxHkFjsLQAWNgtnhTdzmRSro546U45ETKjHMadjRONNK647BBbQLdJ555sxwp4ztn5Y8VPQ80Ah4Tz72tImXcnClw1uIM/Q20OOAB8JR49kPi5kzbfufQ90Ar3KS13pwaG9T2jF0idYqz+L8b0+WcbSJ1jjNI5EWaA+ehVMQjlR0nEck4kUgfwMWvP1CofydVv7gn9pB6A3exZu5iEe5iEe5iEe5iEe5iESWtGxN3seY4Y3exvDg1d7FInJq7mLtYgrtYBtzFsiKN3MX+J9LUXayZu1hE2esG7mKtI82Lvjl/uYtlwF1MkiRJkiQpDMw3gf9lg/avHogAAAAASUVORK5CYII=",
20
+ "mediaAttachment": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACF0lEQVR4nO2bL08cQRiH99JAQyowGAqpqMW1HwDNBwBJSHD8qeELVKAraiuQTV01njsI0GAwVU0q6po0B6kg8JBJN4Rcjntndt8ru8Pv8e+ffTL37mRnriiEEEIIIYSoC/Ac2AYOgQvaQ+i1B2wBk+OSMwec0X6+AS/HsXLOyIdT15UEvCM/Nj0FHZEfPU9BffKj7ykoSyTIQIIMJMhAggwkyECCDCTIQIIMJMhAggwkyECCDCTIQIIMJMhAggwkyECCDCTIQIKemKA+sOx55J2boJWylzWvhDkJ+jDQz55H0lwEdQfP0YEXwHlOgk4qxv166CYGsFB3HjVF0D7wDPicGHcFLBp9rbVd0A9gpswxlXgJYieyt722CvoLvB3IMwv8jIj9CnQie6s8jx5b0PoDud4AlyPivgPTif1VmkePKeiTkS9s+G6GxIWHXKjYY/I8qixkSPEUwlW9qYicu0NiV2v2mTSP6tQaLBzLb+B1ZM4O8OVe7EeHPpPmUd16d0TWuwaWigTKN9txea3Y5VJlyjzyqJci6H1RAeAVMF8ltu488iwYtRksGkTMPPIsFrUZbBIx88izWPRmsElY88izUNJmsEmMmkeeRbJEggwk6D8K6pMffzwFHZIfXU9BW+THhqegyfKferkQvpFPuAkKhI/nmUgKcmaLccC/lbRZHsW0aXCHXg/Cz8p95QghhBBCiOJJcgtCLsL+N1x3iwAAAABJRU5ErkJggg==",
21
+ "link": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACPElEQVR4nO3ZTU4UQRjG8RIW4gXEszjBOxg9g6BrYLbGYOLH6DVIPIRAIpkroBudYQOcwCE6f1Pysuuq6Y+3e6bJ80tm+9bT73RXVVeHICIiIiIiIiIiIiIi/QesA1vACBgDl8A1FWXqV3VtGcaWKWZb77Yr4X/wB8DQwjSWGcdDzLgfM3fVnOfAOY5abtCtKfCszcbcA14Dc+fgXTUIy/4OWPNuzhpwSEs6bNCtQ9cmAW9LDPoHOAOOgS9VfplxK9WxsWOGvyXyHnjOOfPMQD+BbeBhWBExC7ADTDK5543nJG5Wq9yE/B64H1YUsGHLfUps4EaTAYaZ4i9CT9gdnrLXZBN4mbpzQs9k7qSLWhM28CQz56zsY7XgcfuVuKZBnYKfEsW2Q08BLxPX9LFOsXFBobh8blas06oaq1vRFuBbnQZdFRQ6q1FnZRpkeX4UlLkINQrNCgp9vQMNOioo89urQUd3oEHHXg26KiikR6zEJP2oYqNbVTHLpuckPUpk2gk9BbxKXNOHOsW2EsXOOzud898opl5eH3u/aoxCzwCfXV81rOg+ab3ZUdvRR8pu0+OOaab4qNFxQcssf+rOaX7cEcVDpQUHZhN7x6n0CtKmuNLahJz7c+M1PfUa8IDF4vL5HThZ4pHriWUoc+T6xqU5kQ7ty3/2GeqzT7k5aYqjzFieJm5zTslN157tIVa9QTHj7lJWW24+KA7iVh04tTCzJTZoZhlOLdPA/SuqiIiIiIiIiIiIiIiEpfgHLFVfxDn1QdwAAAAASUVORK5CYII=",
22
+ "undo": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACP0lEQVR4nO3YTahMYRgH8HN9Jm5XLJC7sFEiNxtJ+VzISpKVYoGFjRQWUsjSgmSFtYWUZGFFUnchoZsklAUllOSGJC73p8MpX3NmzMy5M2em51ezO+/7/ufpnPc8502SEEIIIYQQQgghhBBCCCGEEMIPOJwUDPOxAydwBY/wCu/wEL1Jp/DTgSbnGIdVOIsXaruAnqQT+GVfA2N7sR/P1G9v0gn8Mord/zlmCo5gWOO+YGVSdv6UFmlXjes34alivMScpMz8q2KRsscp3WOKdhOTkrJS2Tds++2aRXhi7JzB0mydWUmZyPcVW7C2yb2mEWk7cAensBl9ZSxQagSftd8nXMKGtKUoU4HK6HHWhI6PAlV3D2viDqpuNHuzTu3GR2wk68DvYhC3cT/boOuVjpvX6QUaxjnsxEJMrJJpNjbiZB2fMa+xvBMLdCPrvic3mK8Hq3Ex68+qeY8VnVKgQSwrOOsArtZY90PadBa5aNHeYmthAStn3l5jr0qPXPqLWqzo76q5hQSrnXtB1hPluYUJRSxUlAeN7jNNZJ+ZvQXzHC1ikSI1H6j+/H3Zaz6vlVjc7AK6oEj92Zl3JdeanVyXFGldlTZgfTMT66Iinc7Jcr2ZSXVLkTADb3KyLGl00rF2rPBKVP8/h3JyHG90wlZo2Z2E6fhYIcPzhg7btE7L7qTso7iSgVZlKLXsaLaSPe3OVgqYlnOWfr7d2UoDQxUKNNTuXKWBg2kX/dfvcrtzhRBCCCGEEEIIIYSQlNV3D0IwYRMB14IAAAAASUVORK5CYII=",
23
+ "redo": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJ0lEQVR4nO3XzYtNYRzA8TGGBWpSbJSNBVaMha1iISywsbWckn/CRpGiGAsv2aNQRlIslFjIxsLLioWXFPK28vbRaZ6FNOOec895zjn0+9RsptvzPPd7zzn3/kZGQgghhBBCCCGEEEIIIYQQ+gIHuj5Dr+NA1+fodZxeBMICbMQ+nMINPMF7fEnn/IQ36f/XcSK9fh1Gc8XpLBCWYC8u4qN6PuAC9mBRk3FaD2TmEz+Hz/Io1j2JNU3EaS0QJnAVP7Wj2OcS1taJkz0QluMMfujGt3RFjQ8Tp5Azzna81A+vsatqnEKOMAvTp9ZHRzFWNk4hxy11p8Yb+I5H6XlVRD7021/xE+Aanta8ZW/hcNkXNxlnJR4PceDnOIItxdd/yb3G0y08hVcyajLOs4rfMjexG/Nr7j2KnWm9/gXCsnRblFXcgutrbzz7WTbhXm8CmRkTbpfcqxgdJjGvsSJzX1H703jSeaBjJfe5jxWNVSh3ttV42Fkg7Ci5x+W6M1KNMx7sJBCW4kWJ9aeanrYrnLHU75xcgaZKrD1d9xuq6zhDBSoGQHwdsO4DLM7y7luMM2yg8wPWfNf2AzlXnMqBsCqNA38zma1Ay3EKVQ9xfMB6d7t4KOeKUylQmtLfDhgfJrKWaDlO1UAb0vNlLleyluggTmGY0WIbzs5yNW3OVqKjOJUD/XHAMWzF6TRKjP5vcWoFCiGEEEIIIYQQQgghhBBCCGHk3/cLZTX3NeIaKrMAAAAASUVORK5CYII=",
24
+ "clearFormatting": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2klEQVR4nO3bzS4DURTA8YPYSLDyCNh4DkktSXwUC0sSHoUQz6AbW/UgNsKStQQ7X3+ZILHQe087d3DPPf91Z9r+ctsznemIeJ7neZ7neZ7neZ7nlRUwCnSA1cjjbsmg1DgjwMnnvl+AzcBjp3NAagoHK0gpgTo9nqNCWssVKSXQIvAUQNrIESkZkFUkSR3GkJIDWUOSpsIIUmNAdZFCAfuK97YtOURiJGAMuIvgPAATkkskRAJ2FKvnQHKLREjARQTnDZiRHKMmEjCvWD1nknPAUgDpOXQWQDndWpJ7NHcIcA0Mi4VoBmlPLEVapEdgUqxFOqRjsRpxpPUI0g0wJ5YjPt1WAttOSQlRYyUVE44Uz5EUVVdDenzUvr6TlqXkgL0AUNnfScAQcBkBKhcJaClwykUCun0AlYXEx5Hxa59A5SABRwPgfEdqi9WAceBecUJ+K3LE3S51tFcdKn+WtEsc7W/A7LdtykECFojX/WG7MpCAcwXQjyfkzSOhG+1XoRPyppHQjfZdxX7sIaEf7apr7eaQ6GO097FPG0gMMNqLQkI32ge+1q5A6vnv22LCkeI5Uv3rbh3NPkpdSafVDTh//dr+K5LjBD5u1b0lvnI8z/M8z/M8+ZXeAZv5+Z1GOJFfAAAAAElFTkSuQmCC",
25
+ "indent": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVR4nO3WQQ7DIAwFUW7uLrn1r7LIqqqcUJsUM+8AVhhApDUAAABglIoaDkKgm1TU3Q4EGqWihoMQCAAABFn+PygbgaoHktTDhhUNpMxIekjGAnrY0KKBlH3dlqRPRHICHYh00ndEcgIR6UIgIumanjw/3Oj3/rKATqCESNroBB2MQIFxdjpBljx/6UDWdibiDAeySXv030Sc24Fs0t6sQcS5HMgmbMA00QuwsIEFA73ChlUMlI1ADgI5CAQAwHbPcDYCOQjkIJCDQA4COZ579gAAANBW9waRrOCa9HpusgAAAABJRU5ErkJggg==",
26
+ "outdent": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA7ElEQVR4nO3ZwQqDQAxF0fx1t/75Ky0upFTThElHzT3bQSZehRE0AwAAALJ0U+kgBArSTUU7EChLN5UOQiAAADDI5b+DqhHI0SqQpCVxTY9AkpbMxi0CaY1DICfO35/M2ekjDoGcOARy4hDIjuO0DyQnTutA+iFOJpAmmRKnZSAF4mQ2rkswds6jG3hUblyXYOycwyJZx0CRSBZUn2LMnMMiWXdyIs2e7/SRDMeR1mXsRXovYD/SZgnfIlmQJrFZkSx+XY9AL/z2KdDqDcogkINAAAC0O4arEchBIAeBHARyEMgx79gDAACAXd0TF7nf4uYCfIsAAAAASUVORK5CYII=",
27
+ "alignLeft": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiElEQVR4nO3VsREDMAwDMe2/NDOCLm7MOMAI3/wMAACcyqOOgwj0pTzq2w4CncqjjoMIBAB/I2WmTcpMm5SZNikzbVJm2qTM7R4A8JA8SqCFQAuBFgItBFrcXR8AVEiZaZMy0yZlpk3KTJuUmTYpc7sHADwkjxJoIdBCoIVAC4EWd9cHAMD8sg+q0NtM71VCsAAAAABJRU5ErkJggg==",
28
+ "alignCenter": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfUlEQVR4nO3W0QnAMAxDwey/tDtBIxooweZuhPcjrQUAAKdqqOMgAn1UQ33tINCpGuo4iEAAwIv2v+ZvJdCeQIFAgUCBQADQRg0lUCBQIFAgUCBQcHf6AKClumR1UQLtCRQIFAgUCAQAbdRQAgUCBQIFAgUCBXenDwCA1dkDHN+8XSMaKtEAAAAASUVORK5CYII=",
29
+ "alignRight": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAiElEQVR4nO3VQREEMQwDQfMnrYPg2nyiS3VDmM/MAADAqTzqOIhAH+VRXzsIdCqPOg4iEADwUcpMm5SZNikzbVJm2qTMtEmZ2z0AoEAeJdBCoIVAC4EWAi3urg8AnpIy0yZlpk3KTJuUmTYpM21S5nYPACiQRwm0EGgh0EKghUCLu+sDAGD+2Q90dNtMxzL28wAAAABJRU5ErkJggg==",
30
+ ]
31
+
32
+ private static var iconCache: [String: UIImage] = [:]
385
33
 
386
- private static func drawMediaAttachmentIcon(color: UIColor, size: CGSize) -> UIImage {
387
- return drawIcon(size: size, color: color) { ctx, scale in
388
- let frame = CGRect(x: 3 * scale, y: 5 * scale, width: 18 * scale, height: 14 * scale)
389
- let framePath = UIBezierPath(roundedRect: frame, cornerRadius: 1.8 * scale)
390
- ctx.setLineWidth(1.8 * scale)
391
- ctx.addPath(framePath.cgPath)
392
- ctx.strokePath()
393
-
394
- let sunPath = UIBezierPath(arcCenter: CGPoint(x: 8 * scale, y: 10 * scale), radius: 1.5 * scale, startAngle: 0, endAngle: .pi * 2, clockwise: true)
395
- sunPath.fill()
396
-
397
- let mountain = UIBezierPath()
398
- mountain.move(to: CGPoint(x: 6 * scale, y: 16 * scale))
399
- mountain.addLine(to: CGPoint(x: 10.5 * scale, y: 11.5 * scale))
400
- mountain.addLine(to: CGPoint(x: 13 * scale, y: 14 * scale))
401
- mountain.addLine(to: CGPoint(x: 16 * scale, y: 11 * scale))
402
- mountain.addLine(to: CGPoint(x: 18 * scale, y: 13 * scale))
403
- mountain.addLine(to: CGPoint(x: 18 * scale, y: 16 * scale))
404
- mountain.close()
405
- mountain.fill()
406
- }
407
- }
408
-
409
- private static func drawLinkIcon(color: UIColor, size: CGSize) -> UIImage {
410
- return drawIcon(size: size, color: color) { ctx, scale in
411
- let path = UIBezierPath()
412
- // Left chain
413
- path.move(to: CGPoint(x: 3.9 * scale, y: 12 * scale))
414
- path.addCurve(to: CGPoint(x: 7 * scale, y: 8.9 * scale),
415
- controlPoint1: CGPoint(x: 3.9 * scale, y: 10.29 * scale),
416
- controlPoint2: CGPoint(x: 5.29 * scale, y: 8.9 * scale))
417
- path.addLine(to: CGPoint(x: 11 * scale, y: 8.9 * scale))
418
- path.addLine(to: CGPoint(x: 11 * scale, y: 7 * scale))
419
- path.addLine(to: CGPoint(x: 7 * scale, y: 7 * scale))
420
- path.addCurve(to: CGPoint(x: 2 * scale, y: 12 * scale),
421
- controlPoint1: CGPoint(x: 4.24 * scale, y: 7 * scale),
422
- controlPoint2: CGPoint(x: 2 * scale, y: 9.24 * scale))
423
- path.addCurve(to: CGPoint(x: 7 * scale, y: 17 * scale),
424
- controlPoint1: CGPoint(x: 2 * scale, y: 14.76 * scale),
425
- controlPoint2: CGPoint(x: 4.24 * scale, y: 17 * scale))
426
- path.addLine(to: CGPoint(x: 11 * scale, y: 17 * scale))
427
- path.addLine(to: CGPoint(x: 11 * scale, y: 15.1 * scale))
428
- path.addLine(to: CGPoint(x: 7 * scale, y: 15.1 * scale))
429
- path.addCurve(to: CGPoint(x: 3.9 * scale, y: 12 * scale),
430
- controlPoint1: CGPoint(x: 5.29 * scale, y: 15.1 * scale),
431
- controlPoint2: CGPoint(x: 3.9 * scale, y: 13.71 * scale))
432
- path.close()
433
-
434
- // Middle bar
435
- path.move(to: CGPoint(x: 8 * scale, y: 13 * scale))
436
- path.addLine(to: CGPoint(x: 16 * scale, y: 13 * scale))
437
- path.addLine(to: CGPoint(x: 16 * scale, y: 11 * scale))
438
- path.addLine(to: CGPoint(x: 8 * scale, y: 11 * scale))
439
- path.close()
440
-
441
- // Right chain
442
- path.move(to: CGPoint(x: 17 * scale, y: 7 * scale))
443
- path.addLine(to: CGPoint(x: 13 * scale, y: 7 * scale))
444
- path.addLine(to: CGPoint(x: 13 * scale, y: 8.9 * scale))
445
- path.addLine(to: CGPoint(x: 17 * scale, y: 8.9 * scale))
446
- path.addCurve(to: CGPoint(x: 20.1 * scale, y: 12 * scale),
447
- controlPoint1: CGPoint(x: 18.71 * scale, y: 8.9 * scale),
448
- controlPoint2: CGPoint(x: 20.1 * scale, y: 10.29 * scale))
449
- path.addCurve(to: CGPoint(x: 17 * scale, y: 15.1 * scale),
450
- controlPoint1: CGPoint(x: 20.1 * scale, y: 13.71 * scale),
451
- controlPoint2: CGPoint(x: 18.71 * scale, y: 15.1 * scale))
452
- path.addLine(to: CGPoint(x: 13 * scale, y: 15.1 * scale))
453
- path.addLine(to: CGPoint(x: 13 * scale, y: 17 * scale))
454
- path.addLine(to: CGPoint(x: 17 * scale, y: 17 * scale))
455
- path.addCurve(to: CGPoint(x: 22 * scale, y: 12 * scale),
456
- controlPoint1: CGPoint(x: 19.76 * scale, y: 17 * scale),
457
- controlPoint2: CGPoint(x: 22 * scale, y: 14.76 * scale))
458
- path.addCurve(to: CGPoint(x: 17 * scale, y: 7 * scale),
459
- controlPoint1: CGPoint(x: 22 * scale, y: 9.24 * scale),
460
- controlPoint2: CGPoint(x: 19.76 * scale, y: 7 * scale))
461
- path.close()
462
-
463
- path.fill()
464
- }
465
- }
466
-
467
- private static func drawUndoIcon(color: UIColor, size: CGSize) -> UIImage {
468
- return drawIcon(size: size, color: color) { ctx, scale in
469
- let path = UIBezierPath()
470
- path.move(to: CGPoint(x: 12.5 * scale, y: 8 * scale))
471
- path.addCurve(to: CGPoint(x: 5.67 * scale, y: 10.73 * scale),
472
- controlPoint1: CGPoint(x: 9.85 * scale, y: 8 * scale),
473
- controlPoint2: CGPoint(x: 7.45 * scale, y: 9.04 * scale))
474
- path.addLine(to: CGPoint(x: 3 * scale, y: 8 * scale))
475
- path.addLine(to: CGPoint(x: 3 * scale, y: 17 * scale))
476
- path.addLine(to: CGPoint(x: 12 * scale, y: 17 * scale))
477
- path.addLine(to: CGPoint(x: 9.19 * scale, y: 14.19 * scale))
478
- path.addCurve(to: CGPoint(x: 14.25 * scale, y: 12.25 * scale),
479
- controlPoint1: CGPoint(x: 10.55 * scale, y: 12.98 * scale),
480
- controlPoint2: CGPoint(x: 12.33 * scale, y: 12.25 * scale))
481
- path.addCurve(to: CGPoint(x: 21.14 * scale, y: 18.25 * scale),
482
- controlPoint1: CGPoint(x: 17.77 * scale, y: 12.25 * scale),
483
- controlPoint2: CGPoint(x: 20.69 * scale, y: 14.86 * scale))
484
- path.addLine(to: CGPoint(x: 23 * scale, y: 18.25 * scale))
485
- path.addCurve(to: CGPoint(x: 14.25 * scale, y: 10.25 * scale),
486
- controlPoint1: CGPoint(x: 22.54 * scale, y: 13.74 * scale),
487
- controlPoint2: CGPoint(x: 18.9 * scale, y: 10.25 * scale))
488
- path.addCurve(to: CGPoint(x: 12.5 * scale, y: 8 * scale),
489
- controlPoint1: CGPoint(x: 13.66 * scale, y: 9.35 * scale),
490
- controlPoint2: CGPoint(x: 13.05 * scale, y: 8.55 * scale))
491
- path.close()
492
- path.fill()
493
- }
494
- }
495
-
496
- private static func drawRedoIcon(color: UIColor, size: CGSize) -> UIImage {
497
- return drawIcon(size: size, color: color) { ctx, scale in
498
- let path = UIBezierPath()
499
- path.move(to: CGPoint(x: 18.4 * scale, y: 10.6 * scale))
500
- path.addCurve(to: CGPoint(x: 11.5 * scale, y: 8 * scale),
501
- controlPoint1: CGPoint(x: 16.55 * scale, y: 8.99 * scale),
502
- controlPoint2: CGPoint(x: 14.15 * scale, y: 8 * scale))
503
- path.addCurve(to: CGPoint(x: 1.54 * scale, y: 15.22 * scale),
504
- controlPoint1: CGPoint(x: 6.85 * scale, y: 8 * scale),
505
- controlPoint2: CGPoint(x: 2.92 * scale, y: 11.03 * scale))
506
- path.addLine(to: CGPoint(x: 3.9 * scale, y: 16 * scale))
507
- path.addCurve(to: CGPoint(x: 11.5 * scale, y: 10.5 * scale),
508
- controlPoint1: CGPoint(x: 4.95 * scale, y: 12.81 * scale),
509
- controlPoint2: CGPoint(x: 7.95 * scale, y: 10.5 * scale))
510
- path.addCurve(to: CGPoint(x: 16.62 * scale, y: 12.38 * scale),
511
- controlPoint1: CGPoint(x: 13.45 * scale, y: 10.5 * scale),
512
- controlPoint2: CGPoint(x: 15.23 * scale, y: 11.22 * scale))
513
- path.addLine(to: CGPoint(x: 13 * scale, y: 16 * scale))
514
- path.addLine(to: CGPoint(x: 22 * scale, y: 16 * scale))
515
- path.addLine(to: CGPoint(x: 22 * scale, y: 7 * scale))
516
- path.close()
517
- path.fill()
518
- }
519
- }
520
-
521
- private static func drawClearFormattingIcon(color: UIColor, size: CGSize) -> UIImage {
522
- return drawIcon(size: size, color: color) { ctx, scale in
523
- let path = UIBezierPath()
524
- // Diagonal line
525
- path.move(to: CGPoint(x: 3.27 * scale, y: 5 * scale))
526
- path.addLine(to: CGPoint(x: 2 * scale, y: 6.27 * scale))
527
- path.addLine(to: CGPoint(x: 8.97 * scale, y: 13.24 * scale))
528
- path.addLine(to: CGPoint(x: 6.5 * scale, y: 19 * scale))
529
- path.addLine(to: CGPoint(x: 9.5 * scale, y: 19 * scale))
530
- path.addLine(to: CGPoint(x: 11.07 * scale, y: 15.34 * scale))
531
- path.addLine(to: CGPoint(x: 16.73 * scale, y: 21 * scale))
532
- path.addLine(to: CGPoint(x: 18 * scale, y: 19.73 * scale))
533
- path.addLine(to: CGPoint(x: 3.55 * scale, y: 5.27 * scale))
534
- path.close()
535
-
536
- // T shape
537
- path.move(to: CGPoint(x: 6 * scale, y: 5 * scale))
538
- path.addLine(to: CGPoint(x: 6 * scale, y: 5.18 * scale))
539
- path.addLine(to: CGPoint(x: 8.82 * scale, y: 8 * scale))
540
- path.addLine(to: CGPoint(x: 11.22 * scale, y: 8 * scale))
541
- path.addLine(to: CGPoint(x: 10.5 * scale, y: 9.68 * scale))
542
- path.addLine(to: CGPoint(x: 12.6 * scale, y: 11.78 * scale))
543
- path.addLine(to: CGPoint(x: 14.21 * scale, y: 8 * scale))
544
- path.addLine(to: CGPoint(x: 20 * scale, y: 8 * scale))
545
- path.addLine(to: CGPoint(x: 20 * scale, y: 5 * scale))
546
- path.close()
547
-
548
- path.fill()
549
- }
550
- }
551
-
552
- private static func drawIndentIcon(color: UIColor, size: CGSize) -> UIImage {
553
- return drawIcon(size: size, color: color) { ctx, scale in
554
- let path = UIBezierPath()
555
- // Lines
556
- path.move(to: CGPoint(x: 3 * scale, y: 21 * scale))
557
- path.addLine(to: CGPoint(x: 21 * scale, y: 21 * scale))
558
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
559
- path.addLine(to: CGPoint(x: 3 * scale, y: 19 * scale))
560
- path.close()
561
-
562
- // Arrow
563
- path.move(to: CGPoint(x: 3 * scale, y: 8 * scale))
564
- path.addLine(to: CGPoint(x: 3 * scale, y: 16 * scale))
565
- path.addLine(to: CGPoint(x: 7 * scale, y: 12 * scale))
566
- path.close()
567
-
568
- path.move(to: CGPoint(x: 11 * scale, y: 17 * scale))
569
- path.addLine(to: CGPoint(x: 21 * scale, y: 17 * scale))
570
- path.addLine(to: CGPoint(x: 21 * scale, y: 15 * scale))
571
- path.addLine(to: CGPoint(x: 11 * scale, y: 15 * scale))
572
- path.close()
573
-
574
- path.move(to: CGPoint(x: 3 * scale, y: 3 * scale))
575
- path.addLine(to: CGPoint(x: 3 * scale, y: 5 * scale))
576
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
577
- path.addLine(to: CGPoint(x: 21 * scale, y: 3 * scale))
578
- path.close()
579
-
580
- path.move(to: CGPoint(x: 11 * scale, y: 9 * scale))
581
- path.addLine(to: CGPoint(x: 21 * scale, y: 9 * scale))
582
- path.addLine(to: CGPoint(x: 21 * scale, y: 7 * scale))
583
- path.addLine(to: CGPoint(x: 11 * scale, y: 7 * scale))
584
- path.close()
585
-
586
- path.move(to: CGPoint(x: 11 * scale, y: 13 * scale))
587
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
588
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
589
- path.addLine(to: CGPoint(x: 11 * scale, y: 11 * scale))
590
- path.close()
591
-
592
- path.fill()
593
- }
594
- }
595
-
596
- private static func drawOutdentIcon(color: UIColor, size: CGSize) -> UIImage {
597
- return drawIcon(size: size, color: color) { ctx, scale in
598
- let path = UIBezierPath()
599
- path.move(to: CGPoint(x: 11 * scale, y: 17 * scale))
600
- path.addLine(to: CGPoint(x: 21 * scale, y: 17 * scale))
601
- path.addLine(to: CGPoint(x: 21 * scale, y: 15 * scale))
602
- path.addLine(to: CGPoint(x: 11 * scale, y: 15 * scale))
603
- path.close()
604
-
605
- // Arrow pointing left
606
- path.move(to: CGPoint(x: 3 * scale, y: 12 * scale))
607
- path.addLine(to: CGPoint(x: 7 * scale, y: 16 * scale))
608
- path.addLine(to: CGPoint(x: 7 * scale, y: 8 * scale))
609
- path.close()
610
-
611
- path.move(to: CGPoint(x: 3 * scale, y: 21 * scale))
612
- path.addLine(to: CGPoint(x: 21 * scale, y: 21 * scale))
613
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
614
- path.addLine(to: CGPoint(x: 3 * scale, y: 19 * scale))
615
- path.close()
616
-
617
- path.move(to: CGPoint(x: 3 * scale, y: 3 * scale))
618
- path.addLine(to: CGPoint(x: 3 * scale, y: 5 * scale))
619
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
620
- path.addLine(to: CGPoint(x: 21 * scale, y: 3 * scale))
621
- path.close()
622
-
623
- path.move(to: CGPoint(x: 11 * scale, y: 9 * scale))
624
- path.addLine(to: CGPoint(x: 21 * scale, y: 9 * scale))
625
- path.addLine(to: CGPoint(x: 21 * scale, y: 7 * scale))
626
- path.addLine(to: CGPoint(x: 11 * scale, y: 7 * scale))
627
- path.close()
628
-
629
- path.move(to: CGPoint(x: 11 * scale, y: 13 * scale))
630
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
631
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
632
- path.addLine(to: CGPoint(x: 11 * scale, y: 11 * scale))
633
- path.close()
634
-
635
- path.fill()
636
- }
637
- }
638
-
639
- private static func drawAlignLeftIcon(color: UIColor, size: CGSize) -> UIImage {
640
- return drawIcon(size: size, color: color) { ctx, scale in
641
- let path = UIBezierPath()
642
- path.move(to: CGPoint(x: 15 * scale, y: 15 * scale))
643
- path.addLine(to: CGPoint(x: 3 * scale, y: 15 * scale))
644
- path.addLine(to: CGPoint(x: 3 * scale, y: 17 * scale))
645
- path.addLine(to: CGPoint(x: 15 * scale, y: 17 * scale))
646
- path.close()
647
-
648
- path.move(to: CGPoint(x: 15 * scale, y: 7 * scale))
649
- path.addLine(to: CGPoint(x: 3 * scale, y: 7 * scale))
650
- path.addLine(to: CGPoint(x: 3 * scale, y: 9 * scale))
651
- path.addLine(to: CGPoint(x: 15 * scale, y: 9 * scale))
652
- path.close()
653
-
654
- path.move(to: CGPoint(x: 3 * scale, y: 13 * scale))
655
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
656
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
657
- path.addLine(to: CGPoint(x: 3 * scale, y: 11 * scale))
658
- path.close()
659
-
660
- path.move(to: CGPoint(x: 3 * scale, y: 21 * scale))
661
- path.addLine(to: CGPoint(x: 21 * scale, y: 21 * scale))
662
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
663
- path.addLine(to: CGPoint(x: 3 * scale, y: 19 * scale))
664
- path.close()
665
-
666
- path.move(to: CGPoint(x: 3 * scale, y: 3 * scale))
667
- path.addLine(to: CGPoint(x: 3 * scale, y: 5 * scale))
668
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
669
- path.addLine(to: CGPoint(x: 21 * scale, y: 3 * scale))
670
- path.close()
671
-
672
- path.fill()
673
- }
674
- }
675
-
676
- private static func drawAlignCenterIcon(color: UIColor, size: CGSize) -> UIImage {
677
- return drawIcon(size: size, color: color) { ctx, scale in
678
- let path = UIBezierPath()
679
- path.move(to: CGPoint(x: 7 * scale, y: 15 * scale))
680
- path.addLine(to: CGPoint(x: 7 * scale, y: 17 * scale))
681
- path.addLine(to: CGPoint(x: 17 * scale, y: 17 * scale))
682
- path.addLine(to: CGPoint(x: 17 * scale, y: 15 * scale))
683
- path.close()
684
-
685
- path.move(to: CGPoint(x: 3 * scale, y: 21 * scale))
686
- path.addLine(to: CGPoint(x: 21 * scale, y: 21 * scale))
687
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
688
- path.addLine(to: CGPoint(x: 3 * scale, y: 19 * scale))
689
- path.close()
690
-
691
- path.move(to: CGPoint(x: 3 * scale, y: 13 * scale))
692
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
693
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
694
- path.addLine(to: CGPoint(x: 3 * scale, y: 11 * scale))
695
- path.close()
696
-
697
- path.move(to: CGPoint(x: 7 * scale, y: 7 * scale))
698
- path.addLine(to: CGPoint(x: 7 * scale, y: 9 * scale))
699
- path.addLine(to: CGPoint(x: 17 * scale, y: 9 * scale))
700
- path.addLine(to: CGPoint(x: 17 * scale, y: 7 * scale))
701
- path.close()
34
+ static func getIcon(for option: String, color: UIColor, size: CGSize = CGSize(width: 24, height: 24)) -> UIImage? {
35
+ var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
36
+ color.getRed(&r, green: &g, blue: &b, alpha: &a)
37
+ let cacheKey = "\(option)_\(r)_\(g)_\(b)_\(a)_\(size.width)_\(size.height)"
38
+ if let cached = iconCache[cacheKey] { return cached }
702
39
 
703
- path.move(to: CGPoint(x: 3 * scale, y: 3 * scale))
704
- path.addLine(to: CGPoint(x: 3 * scale, y: 5 * scale))
705
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
706
- path.addLine(to: CGPoint(x: 21 * scale, y: 3 * scale))
707
- path.close()
40
+ guard let b64 = iconData[option],
41
+ let data = Data(base64Encoded: b64),
42
+ let original = UIImage(data: data) else { return nil }
708
43
 
709
- path.fill()
44
+ // Render at target size with tint color
45
+ let renderer = UIGraphicsImageRenderer(size: size)
46
+ let tinted = renderer.image { context in
47
+ let rect = CGRect(origin: .zero, size: size)
48
+ original.draw(in: rect)
49
+ color.setFill()
50
+ context.cgContext.setBlendMode(.sourceIn)
51
+ context.cgContext.fill(rect)
710
52
  }
711
- }
712
-
713
- private static func drawAlignRightIcon(color: UIColor, size: CGSize) -> UIImage {
714
- return drawIcon(size: size, color: color) { ctx, scale in
715
- let path = UIBezierPath()
716
- path.move(to: CGPoint(x: 3 * scale, y: 21 * scale))
717
- path.addLine(to: CGPoint(x: 21 * scale, y: 21 * scale))
718
- path.addLine(to: CGPoint(x: 21 * scale, y: 19 * scale))
719
- path.addLine(to: CGPoint(x: 3 * scale, y: 19 * scale))
720
- path.close()
721
-
722
- path.move(to: CGPoint(x: 9 * scale, y: 17 * scale))
723
- path.addLine(to: CGPoint(x: 21 * scale, y: 17 * scale))
724
- path.addLine(to: CGPoint(x: 21 * scale, y: 15 * scale))
725
- path.addLine(to: CGPoint(x: 9 * scale, y: 15 * scale))
726
- path.close()
727
53
 
728
- path.move(to: CGPoint(x: 3 * scale, y: 13 * scale))
729
- path.addLine(to: CGPoint(x: 21 * scale, y: 13 * scale))
730
- path.addLine(to: CGPoint(x: 21 * scale, y: 11 * scale))
731
- path.addLine(to: CGPoint(x: 3 * scale, y: 11 * scale))
732
- path.close()
733
-
734
- path.move(to: CGPoint(x: 9 * scale, y: 9 * scale))
735
- path.addLine(to: CGPoint(x: 21 * scale, y: 9 * scale))
736
- path.addLine(to: CGPoint(x: 21 * scale, y: 7 * scale))
737
- path.addLine(to: CGPoint(x: 9 * scale, y: 7 * scale))
738
- path.close()
739
-
740
- path.move(to: CGPoint(x: 3 * scale, y: 3 * scale))
741
- path.addLine(to: CGPoint(x: 3 * scale, y: 5 * scale))
742
- path.addLine(to: CGPoint(x: 21 * scale, y: 5 * scale))
743
- path.addLine(to: CGPoint(x: 21 * scale, y: 3 * scale))
744
- path.close()
745
-
746
- path.fill()
747
- }
54
+ iconCache[cacheKey] = tinted
55
+ return tinted
748
56
  }
749
57
 
750
- // MARK: - Helper
751
-
752
- private static func drawIcon(size: CGSize, color: UIColor, draw: (CGContext, CGFloat) -> Void) -> UIImage {
753
- let renderer = UIGraphicsImageRenderer(size: size)
754
- return renderer.image { context in
755
- color.setFill()
756
- let scale = size.width / 24.0
757
- draw(context.cgContext, scale)
758
- }
58
+ static func clearCache() {
59
+ iconCache.removeAll()
759
60
  }
760
61
  }