UNPKG

3.24 kBPlain TextView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "ARTText.h"
9
10#import <CoreText/CoreText.h>
11
12@implementation ARTText
13
14- (void)setAlignment:(CTTextAlignment)alignment
15{
16 [self invalidate];
17 _alignment = alignment;
18}
19
20static void ARTFreeTextFrame(ARTTextFrame frame)
21{
22 if (frame.count) {
23 // We must release each line before freeing up this struct
24 for (int i = 0; i < frame.count; i++) {
25 CFRelease(frame.lines[i]);
26 }
27 free(frame.lines);
28 free(frame.widths);
29 }
30}
31
32- (void)setTextFrame:(ARTTextFrame)frame
33{
34 if (frame.lines != _textFrame.lines) {
35 ARTFreeTextFrame(_textFrame);
36 }
37 [self invalidate];
38 _textFrame = frame;
39}
40
41- (void)dealloc
42{
43 ARTFreeTextFrame(_textFrame);
44}
45
46- (void)renderLayerTo:(CGContextRef)context
47{
48 ARTTextFrame frame = self.textFrame;
49
50 if ((!self.fill && !self.stroke) || !frame.count) {
51 return;
52 }
53
54 // to-do: draw along a path
55
56 CGTextDrawingMode mode = kCGTextStroke;
57 if (self.fill) {
58 if ([self.fill applyFillColor:context]) {
59 mode = kCGTextFill;
60 } else {
61
62 for (int i = 0; i < frame.count; i++) {
63 CGContextSaveGState(context);
64 // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
65 CGContextScaleCTM(context, 1.0, -1.0);
66 CGContextSetTextDrawingMode(context, kCGTextClip);
67 [self renderLineTo:context atIndex:i];
68 // Inverse the coordinate space back to the original before filling
69 CGContextScaleCTM(context, 1.0, -1.0);
70 [self.fill paint:context];
71 // Restore the state so that the next line can be clipped separately
72 CGContextRestoreGState(context);
73 }
74
75 if (!self.stroke) {
76 return;
77 }
78 }
79 }
80 if (self.stroke) {
81 CGContextSetStrokeColorWithColor(context, self.stroke);
82 CGContextSetLineWidth(context, self.strokeWidth);
83 CGContextSetLineCap(context, self.strokeCap);
84 CGContextSetLineJoin(context, self.strokeJoin);
85 ARTCGFloatArray dash = self.strokeDash;
86 if (dash.count) {
87 CGContextSetLineDash(context, 0, dash.array, dash.count);
88 }
89 if (mode == kCGTextFill) {
90 mode = kCGTextFillStroke;
91 }
92 }
93
94 CGContextSetTextDrawingMode(context, mode);
95
96 // Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
97 CGContextScaleCTM(context, 1.0, -1.0);
98 for (int i = 0; i < frame.count; i++) {
99 [self renderLineTo:context atIndex:i];
100 }
101}
102
103- (void)renderLineTo:(CGContextRef)context atIndex:(int)index
104{
105 ARTTextFrame frame = self.textFrame;
106 CGFloat shift;
107 switch (self.alignment) {
108 case kCTTextAlignmentRight:
109 shift = frame.widths[index];
110 break;
111 case kCTTextAlignmentCenter:
112 shift = (frame.widths[index] / 2);
113 break;
114 default:
115 shift = 0;
116 break;
117 }
118 // We should consider snapping this shift to device pixels to improve rendering quality
119 // when a line has subpixel width.
120 CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index);
121 CTLineRef line = frame.lines[index];
122 CTLineDraw(line, context);
123}
124
125@end