1 /*
2 * Copyright 2017 Patrick O. Perry.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18 #include "rutf8.h"
19
20
rutf8_text_width(const struct utf8lite_text * text,int flags)21 int rutf8_text_width(const struct utf8lite_text *text, int flags)
22 {
23 struct utf8lite_graphscan scan;
24 int err = 0, width, w;
25
26 utf8lite_graphscan_make(&scan, text);
27 width = 0;
28 while (utf8lite_graphscan_advance(&scan)) {
29 TRY(utf8lite_graph_measure(&scan.current, flags, &w));
30 if (w < 0) {
31 return -1;
32 }
33 if (width > INT_MAX - w) {
34 Rf_error("width exceeds maximum (%d)", INT_MAX);
35 }
36 width += w;
37 }
38 exit:
39 CHECK_ERROR(err);
40 return width;
41 }
42
43
rutf8_text_lwidth(const struct utf8lite_text * text,int flags,int limit,int ellipsis)44 int rutf8_text_lwidth(const struct utf8lite_text *text, int flags,
45 int limit, int ellipsis)
46 {
47 struct utf8lite_graphscan scan;
48 int err = 0, width, w;
49
50 utf8lite_graphscan_make(&scan, text);
51 width = 0;
52 while (utf8lite_graphscan_advance(&scan)) {
53 TRY(utf8lite_graph_measure(&scan.current, flags, &w));
54 if (w < 0) {
55 return -1;
56 }
57 if (width > limit - w) {
58 return width + ellipsis;
59 }
60 width += w;
61 }
62 exit:
63 CHECK_ERROR(err);
64 return width;
65 }
66
67
rutf8_text_rwidth(const struct utf8lite_text * text,int flags,int limit,int ellipsis)68 int rutf8_text_rwidth(const struct utf8lite_text *text, int flags,
69 int limit, int ellipsis)
70 {
71 struct utf8lite_graphscan scan;
72 int err = 0, width, w;
73
74 utf8lite_graphscan_make(&scan, text);
75 utf8lite_graphscan_skip(&scan);
76 width = 0;
77 while (utf8lite_graphscan_retreat(&scan)) {
78 TRY(utf8lite_graph_measure(&scan.current, flags, &w));
79 if (w < 0) {
80 return -1;
81 }
82 if (width > limit - w) {
83 return width + ellipsis;
84 }
85 width += w;
86 }
87 exit:
88 return width;
89 }
90
91
rutf8_text_lrender(struct utf8lite_render * r,const struct utf8lite_text * text,int width_min,int quote,int centre)92 static void rutf8_text_lrender(struct utf8lite_render *r,
93 const struct utf8lite_text *text,
94 int width_min, int quote, int centre)
95 {
96 struct utf8lite_graphscan scan;
97 int err = 0, w, fullwidth, width, quotes;
98
99 assert(width_min >= 0);
100
101 quotes = quote ? 2 : 0;
102 width = 0;
103
104 if (centre && width_min > 0) {
105 fullwidth = rutf8_text_width(text, r->flags);
106 // ensure fullwidth + quotes doesn't overflow
107 if (fullwidth <= width_min - quotes) {
108 width = (width_min - fullwidth - quotes) / 2;
109 TRY(utf8lite_render_chars(r, ' ', width));
110 }
111 }
112
113 if (quote) {
114 TRY(utf8lite_render_raw(r, "\"", 1));
115 assert(width < INT_MAX); // width <= width_min / 2
116 width++;
117 }
118
119 utf8lite_graphscan_make(&scan, text);
120 while (utf8lite_graphscan_advance(&scan)) {
121 TRY(utf8lite_graph_measure(&scan.current, r->flags, &w));
122 TRY(utf8lite_render_graph(r, &scan.current));
123
124 assert(w >= 0);
125 if (width <= width_min - w) {
126 width += w;
127 } else {
128 width = width_min; // truncate to avoid overflow
129 }
130 }
131
132 if (quote) {
133 TRY(utf8lite_render_raw(r, "\"", 1));
134 if (width < width_min) { // avoid overflow
135 width++;
136 }
137 }
138
139 TRY(utf8lite_render_chars(r, ' ', width_min - width));
140 exit:
141 CHECK_ERROR(err);
142 }
143
144
rutf8_text_rrender(struct utf8lite_render * r,const struct utf8lite_text * text,int width_min,int quote)145 static void rutf8_text_rrender(struct utf8lite_render *r,
146 const struct utf8lite_text *text,
147 int width_min, int quote)
148 {
149 struct utf8lite_graphscan scan;
150 int err = 0, fullwidth, quotes;
151
152 quotes = quote ? 2 : 0;
153
154 if (width_min > 0) {
155 fullwidth = rutf8_text_width(text, r->flags);
156 // ensure fullwidth + quotes doesn't overflow
157 if (fullwidth <= width_min - quotes) {
158 fullwidth += quotes;
159 TRY(utf8lite_render_chars(r, ' ',
160 width_min - fullwidth));
161 }
162 }
163
164 if (quote) {
165 TRY(utf8lite_render_raw(r, "\"", 1));
166 }
167
168 utf8lite_graphscan_make(&scan, text);
169 while (utf8lite_graphscan_advance(&scan)) {
170 TRY(utf8lite_render_graph(r, &scan.current));
171 }
172
173 if (quote) {
174 TRY(utf8lite_render_raw(r, "\"", 1));
175 }
176 exit:
177 CHECK_ERROR(err);
178 }
179
180
rutf8_text_render(struct utf8lite_render * r,const struct utf8lite_text * text,int width,int quote,enum rutf8_justify_type justify)181 void rutf8_text_render(struct utf8lite_render *r,
182 const struct utf8lite_text *text,
183 int width, int quote, enum rutf8_justify_type justify)
184 {
185 int centre;
186 if (justify == RUTF8_JUSTIFY_RIGHT) {
187 rutf8_text_rrender(r, text, width, quote);
188 } else {
189 centre = (justify == RUTF8_JUSTIFY_CENTRE);
190 rutf8_text_lrender(r, text, width, quote, centre);
191 }
192 }
193
rutf8_text_lformat(struct utf8lite_render * r,const struct utf8lite_text * text,int trim,int chars,int quote,const char * ellipsis,size_t nellipsis,int wellipsis,int flags,int width_max,int centre)194 static SEXP rutf8_text_lformat(struct utf8lite_render *r,
195 const struct utf8lite_text *text,
196 int trim, int chars, int quote,
197 const char *ellipsis, size_t nellipsis,
198 int wellipsis, int flags, int width_max,
199 int centre)
200 {
201 SEXP ans = R_NilValue;
202 struct utf8lite_graphscan scan;
203 int err = 0, w, trunc, bfill, efill, fullwidth, width, quotes;
204
205 quotes = quote ? 2 : 0;
206
207 bfill = 0;
208 if (centre && !trim) {
209 fullwidth = (rutf8_text_lwidth(text, flags, chars, wellipsis)
210 + quotes);
211 if (fullwidth < width_max) {
212 bfill = (width_max - fullwidth) / 2;
213 TRY(utf8lite_render_chars(r, ' ', bfill));
214 }
215 }
216
217 width = 0;
218 trunc = 0;
219 utf8lite_graphscan_make(&scan, text);
220
221 while (!trunc && utf8lite_graphscan_advance(&scan)) {
222 TRY(utf8lite_graph_measure(&scan.current, flags, &w));
223
224 if (width > chars - w) {
225 w = wellipsis;
226 TRY(utf8lite_render_raw(r, ellipsis, nellipsis));
227 trunc = 1;
228 } else {
229 TRY(utf8lite_render_graph(r, &scan.current));
230 }
231
232 width += w;
233 }
234
235 if (!trim) {
236 efill = width_max - width - quotes - bfill;
237 TRY(utf8lite_render_chars(r, ' ', efill));
238 }
239
240 ans = mkCharLenCE((char *)r->string, r->length, CE_UTF8);
241 utf8lite_render_clear(r);
242 exit:
243 CHECK_ERROR(err);
244 return ans;
245 }
246
247
rutf8_text_rformat(struct utf8lite_render * r,const struct utf8lite_text * text,int trim,int chars,int quote,const char * ellipsis,size_t nellipsis,int wellipsis,int flags,int width_max)248 static SEXP rutf8_text_rformat(struct utf8lite_render *r,
249 const struct utf8lite_text *text,
250 int trim, int chars, int quote,
251 const char *ellipsis, size_t nellipsis,
252 int wellipsis, int flags, int width_max)
253 {
254 SEXP ans = R_NilValue;
255 struct utf8lite_graphscan scan;
256 int err = 0, w, width, quotes, trunc;
257
258 quotes = quote ? 2 : 0;
259
260 utf8lite_graphscan_make(&scan, text);
261 utf8lite_graphscan_skip(&scan);
262 width = 0;
263 trunc = 0;
264
265 while (!trunc && utf8lite_graphscan_retreat(&scan)) {
266 TRY(utf8lite_graph_measure(&scan.current, flags, &w));
267
268 if (width > chars - w) {
269 w = wellipsis;
270 trunc = 1;
271 }
272
273 width += w;
274 }
275
276 if (!trim) {
277 TRY(utf8lite_render_chars(r, ' ', width_max - width - quotes));
278 }
279
280 if (trunc) {
281 TRY(utf8lite_render_raw(r, ellipsis, nellipsis));
282 }
283
284 while (utf8lite_graphscan_advance(&scan)) {
285 TRY(utf8lite_render_graph(r, &scan.current));
286 }
287
288 ans = mkCharLenCE((char *)r->string, r->length, CE_UTF8);
289 utf8lite_render_clear(r);
290 exit:
291 CHECK_ERROR(err);
292 return ans;
293 }
294
295
rutf8_text_format(struct utf8lite_render * r,const struct utf8lite_text * text,int trim,int chars,enum rutf8_justify_type justify,int quote,const char * ellipsis,size_t nellipsis,int wellipsis,int flags,int width_max)296 SEXP rutf8_text_format(struct utf8lite_render *r,
297 const struct utf8lite_text *text,
298 int trim, int chars, enum rutf8_justify_type justify,
299 int quote, const char *ellipsis, size_t nellipsis,
300 int wellipsis, int flags, int width_max)
301 {
302 int centre;
303
304 if (justify == RUTF8_JUSTIFY_RIGHT) {
305 return rutf8_text_rformat(r, text, trim, chars, quote,
306 ellipsis, nellipsis, wellipsis,
307 flags, width_max);
308 } else {
309 centre = (justify == RUTF8_JUSTIFY_CENTRE);
310 return rutf8_text_lformat(r, text, trim, chars, quote,
311 ellipsis, nellipsis, wellipsis,
312 flags, width_max, centre);
313 }
314 }
315