Skip to content

Commit 841a1dd

Browse files
committed
Better checks for the size of the input
1 parent 0cf6a49 commit 841a1dd

File tree

6 files changed

+135
-90
lines changed

6 files changed

+135
-90
lines changed

.Rbuildignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
^O$
1414
^revdep$
1515
^CRAN-SUBMISSION$
16+
^compile_commands\.json$
17+
^\.cache$

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ autobrew
88
configure.log
99
inst/doc
1010
O
11+
compile_commands.json
12+
.cache

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ VignetteBuilder:
3030
knitr
3131
Encoding: UTF-8
3232
Roxygen: list(markdown = TRUE)
33-
RoxygenNote: 7.3.1
33+
RoxygenNote: 7.3.2
3434
SystemRequirements: freetype2, harfbuzz, fribidi
35+
Config/build/compilation-database: true

NEWS.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# textshaping (development version)
22

3+
* Make compiled code somewhat less assumptive about the correctness of the input
4+
35
# textshaping 0.4.0
46

57
* Full rewrite of `shape_text()` to allow proper font-fallback, bidi text
@@ -11,9 +13,9 @@
1113

1214
# textshaping 0.3.6
1315

14-
* Fix a bug in fallback font loading which would crash the process if the font
16+
* Fix a bug in fallback font loading which would crash the process if the font
1517
failed to load (#23)
16-
* Fixed bug that would reset fallback to the original font for short strings
18+
* Fixed bug that would reset fallback to the original font for short strings
1719
(#25)
1820

1921
# textshaping 0.3.5
@@ -63,5 +65,5 @@
6365

6466
# textshaping 0.1.0
6567

66-
* First release. Provide access to HarfBuzz shaping and FriBidi bidirectional
68+
* First release. Provide access to HarfBuzz shaping and FriBidi bidirectional
6769
script support.

src/face_feature.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ writable::list get_face_features_c(strings path, integers index) {
2222
using namespace cpp11;
2323

2424
writable::list get_face_features_c(strings path, integers index) {
25+
if (path.size() != index.size()) {
26+
cpp11::stop("`path` and `index` must be the same length");
27+
}
28+
2529
writable::list features(path.size());
30+
if (path.size() == 0) {
31+
return features; // Early exit
32+
}
33+
2634
std::vector<hb_tag_t> tags;
2735
unsigned int n_tags = 0;
2836
char tag_temp[5];

src/string_metrics.cpp

Lines changed: 116 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "cpp11/protect.hpp"
12
#define R_NO_REMAP
23

34
#include "string_metrics.h"
@@ -110,6 +111,10 @@ std::vector< std::vector<FontFeature> > create_font_features(list_of<list> featu
110111
std::vector<FontSettings> create_font_settings(strings path, integers index, std::vector< std::vector<FontFeature> >& features) {
111112
std::vector<FontSettings> res;
112113

114+
if (path.size() != index.size() || path.size() != features.size()) {
115+
cpp11::stop("`path`, `index`, and `features` must all be of the same length");
116+
}
117+
113118
for (R_xlen_t i = 0; i < path.size(); ++i) {
114119
res.emplace_back();
115120
strncpy(res.back().file, Rf_translateCharUTF8(path[i]), PATH_MAX);
@@ -129,77 +134,99 @@ list get_string_shape_c(strings string, integers id, strings path, integers inde
129134
doubles indent, doubles hanging, doubles space_before,
130135
doubles space_after) {
131136
int n_strings = string.size();
132-
auto all_features = create_font_features(features);
133-
auto fonts = create_font_settings(path, index, all_features);
134137

135138
// Return Columns
136139
writable::integers glyph, glyph_id, metric_id, string_id, fontindex;
137140
writable::doubles x_offset, y_offset, widths, heights, left_bearings, right_bearings,
138141
top_bearings, bottom_bearings, left_border, top_border, pen_x, pen_y, fontsize,
139142
advance, ascender, descender;
140-
writable::strings fontpath;
141-
142-
// Shape the text
143-
int cur_id = id[0] - 1; // make sure it differs from first
144-
bool success = false;
145-
146-
HarfBuzzShaper& shaper = get_hb_shaper();
147-
for (int i = 0; i < n_strings; ++i) {
148-
const char* this_string = Rf_translateCharUTF8(string[i]);
149-
int this_id = id[i];
150-
if (cur_id == this_id) {
151-
success = shaper.add_string(this_string, fonts[i], size[i], tracking[i], cpp11::is_na(string[i]));
152-
153-
if (!success) {
154-
Rf_error("Failed to shape string (%s) with font file (%s) with freetype error %i", this_string, Rf_translateCharUTF8(path[i]), shaper.error_code);
155-
}
156-
} else {
157-
cur_id = this_id;
158-
success = shaper.shape_string(this_string, fonts[i], size[i], res[i],
159-
lineheight[i], align[i], hjust[i], vjust[i],
160-
width[i] * 64.0, tracking[i], indent[i] * 64.0,
161-
hanging[i] * 64.0, space_before[i] * 64.0,
162-
space_after[i] * 64.0, cpp11::is_na(string[i]));
163-
164-
if (!success) {
165-
Rf_error("Failed to shape string (%s) with font file (%s) with freetype error %i", this_string, Rf_translateCharUTF8(STRING_ELT(path, i)), shaper.error_code);
166-
}
143+
writable::strings fontpath, str;
144+
145+
if (n_strings != 0) {
146+
if (n_strings != id.size() ||
147+
n_strings != path.size() ||
148+
n_strings != index.size() ||
149+
n_strings != features.size() ||
150+
n_strings != size.size() ||
151+
n_strings != res.size() ||
152+
n_strings != lineheight.size() ||
153+
n_strings != align.size() ||
154+
n_strings != hjust.size() ||
155+
n_strings != vjust.size() ||
156+
n_strings != width.size() ||
157+
n_strings != tracking.size() ||
158+
n_strings != indent.size() ||
159+
n_strings != hanging.size() ||
160+
n_strings != space_before.size() ||
161+
n_strings != space_after.size()
162+
) {
163+
cpp11::stop("All input must be the same size");
167164
}
168-
bool store_string = i == n_strings - 1 || cur_id != INTEGER(id)[i + 1];
169-
if (store_string) {
170-
success = shaper.finish_string();
171-
if (!success) {
172-
Rf_error("Failed to finalise string shaping");
165+
auto all_features = create_font_features(features);
166+
auto fonts = create_font_settings(path, index, all_features);
167+
168+
// Shape the text
169+
int cur_id = id[0] - 1; // make sure it differs from first
170+
bool success = false;
171+
172+
HarfBuzzShaper& shaper = get_hb_shaper();
173+
for (int i = 0; i < n_strings; ++i) {
174+
const char* this_string = Rf_translateCharUTF8(string[i]);
175+
int this_id = id[i];
176+
if (cur_id == this_id) {
177+
success = shaper.add_string(this_string, fonts[i], size[i], tracking[i], cpp11::is_na(string[i]));
178+
179+
if (!success) {
180+
cpp11::stop("Failed to shape string (%s) with font file (%s) with freetype error %i", this_string, Rf_translateCharUTF8(path[i]), shaper.error_code);
181+
}
182+
} else {
183+
cur_id = this_id;
184+
success = shaper.shape_string(this_string, fonts[i], size[i], res[i],
185+
lineheight[i], align[i], hjust[i], vjust[i],
186+
width[i] * 64.0, tracking[i], indent[i] * 64.0,
187+
hanging[i] * 64.0, space_before[i] * 64.0,
188+
space_after[i] * 64.0, cpp11::is_na(string[i]));
189+
190+
if (!success) {
191+
cpp11::stop("Failed to shape string (%s) with font file (%s) with freetype error %i", this_string, Rf_translateCharUTF8(STRING_ELT(path, i)), shaper.error_code);
192+
}
173193
}
174-
int n_glyphs = shaper.glyph_id.size();
175-
for (int j = 0; j < n_glyphs; j++) {
176-
if (shaper.must_break[j]) continue; // Don't add any linebreak chars as they often map to null glyph
177-
glyph.push_back((int) shaper.glyph_cluster[j] + 1);
178-
glyph_id.push_back((int) shaper.glyph_id[j]);
179-
metric_id.push_back(pen_x.size() + 1);
180-
string_id.push_back(shaper.string_id[j] + 1);
181-
x_offset.push_back(double(shaper.x_pos[j]) / 64.0);
182-
y_offset.push_back(double(shaper.y_pos[j]) / 64.0);
183-
fontpath.push_back(shaper.fontfile[j]);
184-
fontindex.push_back((int) shaper.fontindex[j]);
185-
fontsize.push_back(shaper.fontsize[j]);
186-
advance.push_back(double(shaper.advance[j]) / 64.0);
187-
ascender.push_back(double(shaper.ascender[j]) / 64.0);
188-
descender.push_back(double(shaper.descender[j]) / 64.0);
194+
bool store_string = i == n_strings - 1 || cur_id != INTEGER(id)[i + 1];
195+
if (store_string) {
196+
success = shaper.finish_string();
197+
if (!success) {
198+
cpp11::stop("Failed to finalise string shaping");
199+
}
200+
int n_glyphs = shaper.glyph_id.size();
201+
for (int j = 0; j < n_glyphs; j++) {
202+
if (shaper.must_break[j]) continue; // Don't add any linebreak chars as they often map to null glyph
203+
glyph.push_back((int) shaper.glyph_cluster[j] + 1);
204+
glyph_id.push_back((int) shaper.glyph_id[j]);
205+
metric_id.push_back(pen_x.size() + 1);
206+
string_id.push_back(shaper.string_id[j] + 1);
207+
x_offset.push_back(double(shaper.x_pos[j]) / 64.0);
208+
y_offset.push_back(double(shaper.y_pos[j]) / 64.0);
209+
fontpath.push_back(shaper.fontfile[j]);
210+
fontindex.push_back((int) shaper.fontindex[j]);
211+
fontsize.push_back(shaper.fontsize[j]);
212+
advance.push_back(double(shaper.advance[j]) / 64.0);
213+
ascender.push_back(double(shaper.ascender[j]) / 64.0);
214+
descender.push_back(double(shaper.descender[j]) / 64.0);
215+
}
216+
widths.push_back(double(shaper.width) / 64.0);
217+
heights.push_back(double(shaper.height) / 64.0);
218+
left_bearings.push_back(double(shaper.left_bearing) / 64.0);
219+
right_bearings.push_back(double(shaper.right_bearing) / 64.0);
220+
top_bearings.push_back(double(shaper.top_bearing) / 64.0);
221+
bottom_bearings.push_back(double(shaper.bottom_bearing) / 64.0);
222+
left_border.push_back(double(shaper.left_border) / 64.0);
223+
top_border.push_back(double(shaper.top_border) / 64.0);
224+
pen_x.push_back(double(shaper.pen_x) / 64.0);
225+
pen_y.push_back(double(shaper.pen_y) / 64.0);
189226
}
190-
widths.push_back(double(shaper.width) / 64.0);
191-
heights.push_back(double(shaper.height) / 64.0);
192-
left_bearings.push_back(double(shaper.left_bearing) / 64.0);
193-
right_bearings.push_back(double(shaper.right_bearing) / 64.0);
194-
top_bearings.push_back(double(shaper.top_bearing) / 64.0);
195-
bottom_bearings.push_back(double(shaper.bottom_bearing) / 64.0);
196-
left_border.push_back(double(shaper.left_border) / 64.0);
197-
top_border.push_back(double(shaper.top_border) / 64.0);
198-
pen_x.push_back(double(shaper.pen_x) / 64.0);
199-
pen_y.push_back(double(shaper.pen_y) / 64.0);
200227
}
228+
str = writable::strings(pen_x.size());
201229
}
202-
writable::strings str(pen_x.size());
203230

204231
writable::data_frame string_df({
205232
"string"_nm = str,
@@ -241,34 +268,37 @@ list get_string_shape_c(strings string, integers id, strings path, integers inde
241268
doubles get_line_width_c(strings string, strings path, integers index, doubles size,
242269
doubles res, logicals include_bearing) {
243270
int n_strings = string.size();
244-
bool one_path = path.size() == 1;
245-
const char* first_path = Rf_translateCharUTF8(path[0]);
246-
int first_index = index[0];
247-
bool one_size = size.size() == 1;
248-
double first_size = size[0];
249-
bool one_res = res.size() == 1;
250-
double first_res = res[0];
251-
bool one_bear = include_bearing.size() == 1;
252-
int first_bear = include_bearing[0];
253-
254271
writable::doubles widths;
255-
int error = 1;
256-
double width = 0;
257-
258-
for (int i = 0; i < n_strings; ++i) {
259-
error = string_width(
260-
Rf_translateCharUTF8(string[i]),
261-
one_path ? first_path : Rf_translateCharUTF8(path[i]),
262-
one_path ? first_index : index[i],
263-
one_size ? first_size : size[i],
264-
one_res ? first_res : res[i],
265-
one_bear ? first_bear : static_cast<int>(include_bearing[0]),
266-
&width
267-
);
268-
if (error) {
269-
Rf_error("Failed to calculate width of string (%s) with font file (%s) with freetype error %i", Rf_translateCharUTF8(string[i]), Rf_translateCharUTF8(path[i]), error);
272+
if (n_strings != 0) {
273+
bool one_path = path.size() == 1;
274+
const char* first_path = Rf_translateCharUTF8(path[0]);
275+
int first_index = index[0];
276+
bool one_size = size.size() == 1;
277+
double first_size = size[0];
278+
bool one_res = res.size() == 1;
279+
double first_res = res[0];
280+
bool one_bear = include_bearing.size() == 1;
281+
int first_bear = include_bearing[0];
282+
283+
284+
int error = 1;
285+
double width = 0;
286+
287+
for (int i = 0; i < n_strings; ++i) {
288+
error = string_width(
289+
Rf_translateCharUTF8(string[i]),
290+
one_path ? first_path : Rf_translateCharUTF8(path[i]),
291+
one_path ? first_index : index[i],
292+
one_size ? first_size : size[i],
293+
one_res ? first_res : res[i],
294+
one_bear ? first_bear : static_cast<int>(include_bearing[0]),
295+
&width
296+
);
297+
if (error) {
298+
cpp11::stop("Failed to calculate width of string (%s) with font file (%s) with freetype error %i", Rf_translateCharUTF8(string[i]), Rf_translateCharUTF8(path[i]), error);
299+
}
300+
widths.push_back(width);
270301
}
271-
widths.push_back(width);
272302
}
273303

274304
return widths;

0 commit comments

Comments
 (0)