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