48 CF_small_caps = 0x200000,
52static const PN_stdfloat small_accent_scale = 0.6f;
55static const PN_stdfloat tiny_accent_scale = 0.4;
58static const PN_stdfloat squash_accent_scale_x = 0.8f;
59static const PN_stdfloat squash_accent_scale_y = 0.5f;
63static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
64static const PN_stdfloat small_squash_accent_scale_y = 0.3;
68static const PN_stdfloat ligature_advance_scale = 0.6f;
76isspacew(
unsigned int ch) {
77 return isascii(ch) && isspace(ch);
85isbreakpoint(
unsigned int ch) {
86 return (ch ==
' ' || ch ==
'\t' ||
87 ch == (
unsigned int)text_soft_hyphen_key ||
88 ch == (
unsigned int)text_soft_break_key);
98 _usage_hint(
Geom::UH_static),
100 _dynamic_merge(text_dynamic_merge),
101 _multiline_mode(true)
112 _initial_cprops(copy._initial_cprops),
113 _text_string(copy._text_string),
114 _text_block(copy._text_block),
117 _next_row_ypos(copy._next_row_ypos),
118 _encoder(copy._encoder),
119 _usage_hint(copy._usage_hint),
120 _max_rows(copy._max_rows),
121 _dynamic_merge(copy._dynamic_merge),
122 _multiline_mode(copy._multiline_mode)
131 _initial_cprops = copy._initial_cprops;
132 _text_string = copy._text_string;
133 _text_block = copy._text_block;
136 _next_row_ypos = copy._next_row_ypos;
137 _encoder = copy._encoder;
138 _usage_hint = copy._usage_hint;
139 _max_rows = copy._max_rows;
140 _dynamic_merge = copy._dynamic_merge;
141 _multiline_mode = copy._multiline_mode;
158 _next_row_ypos = 0.0f;
160 _text_string.clear();
178 wstring::const_iterator si = wtext.begin();
179 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
181 while (si != wtext.end()) {
186 <<
"pop_properties encountered without preceding push_properties.\n";
187 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
191 return wordwrap_text();
208set_wsubstr(
const wstring &wtext,
int start,
int count) {
209 nassertr(start >= 0 && start <= (
int)_text_string.size(),
false);
210 nassertr(count >= 0 && start + count <= (
int)_text_string.size(),
false);
215 wstring::const_iterator si = wtext.begin();
216 scan_wtext(substr, si, wtext.end(), _initial_cprops);
217 while (si != wtext.end()) {
219 <<
"pop_properties encountered without preceding push_properties.\n";
220 scan_wtext(substr, si, wtext.end(), _initial_cprops);
223 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
224 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
226 return wordwrap_text();
242 for (
const TextCharacter &tch : _text_string) {
243 if (tch._graphic ==
nullptr) {
244 if (
sizeof(
wchar_t) >= 4 || (tch._character & ~0xffff) == 0) {
245 wtext += (wchar_t)tch._character;
248 char32_t v = (char32_t)tch._character - 0x10000u;
249 wtext += (wchar_t)((v >> 10u) | 0xd800u);
250 wtext += (wchar_t)((v & 0x3ffu) | 0xdc00u);
274 TextBlock::const_iterator bi;
275 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
276 const TextRow &row = (*bi);
277 if (bi != _text_block.begin()) {
281 for (
const TextCharacter &tch : row._string) {
282 if (tch._graphic ==
nullptr) {
283 if (
sizeof(
wchar_t) >= 4 || (tch._character & ~0xffff) == 0) {
284 wtext += (wchar_t)tch._character;
287 char32_t v = (char32_t)tch._character - 0x10000u;
288 wtext += (wchar_t)((v >> 10u) | 0xd800u);
289 wtext += (wchar_t)((v & 0x3ffu) | 0xdc00u);
310 PT(ComputedProperties) current_cprops = _initial_cprops;
312 for (
const TextCharacter &tch : _text_string) {
313 current_cprops->append_delta(wtext, tch._cprops);
314 if (tch._graphic ==
nullptr) {
315 if (
sizeof(
wchar_t) >= 4 || (tch._character & ~0xffff) == 0) {
316 wtext += (wchar_t)tch._character;
319 char32_t v = (char32_t)tch._character - 0x10000u;
320 wtext += (wchar_t)((v >> 10u) | 0xd800u);
321 wtext += (wchar_t)((v & 0x3ffu) | 0xdc00u);
324 wtext.push_back(text_embed_graphic_key);
325 wtext += tch._graphic_wname;
326 wtext.push_back(text_embed_graphic_key);
328 current_cprops = tch._cprops;
330 current_cprops->append_delta(wtext, _initial_cprops);
352 PT(ComputedProperties) current_cprops = _initial_cprops;
354 TextBlock::const_iterator bi;
355 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
356 const TextRow &row = (*bi);
357 if (bi != _text_block.begin()) {
358 current_cprops->append_delta(wtext, _initial_cprops);
359 current_cprops = _initial_cprops;
363 for (
const TextCharacter &tch : row._string) {
364 current_cprops->append_delta(wtext, tch._cprops);
365 if (tch._graphic ==
nullptr) {
366 if (
sizeof(
wchar_t) >= 4 || (tch._character & ~0xffff) == 0) {
367 wtext += (wchar_t)tch._character;
370 char32_t v = (char32_t)tch._character - 0x10000u;
371 wtext += (wchar_t)((v >> 10u) | 0xd800u);
372 wtext += (wchar_t)((v & 0x3ffu) | 0xdc00u);
375 wtext.push_back(text_embed_graphic_key);
376 wtext += tch._graphic_wname;
377 wtext.push_back(text_embed_graphic_key);
379 current_cprops = tch._cprops;
382 current_cprops->append_delta(wtext, _initial_cprops);
397calc_r_c(
int &r,
int &c,
int n)
const {
398 nassertr(n >= 0 && n <= (
int)_text_string.size(),
false);
400 if (n == (
int)_text_string.size()) {
402 if (_text_string.empty()) {
406 r = _text_block.size() - 1;
407 c = _text_block[r]._string.size();
419 while (r + 1 < (
int)_text_block.size() &&
420 _text_block[r + 1]._row_start < n) {
424 const TextRow &row = _text_block[r];
425 bool is_real_char =
true;
427 nassertr(n > 0,
false);
428 if (row._got_soft_hyphens) {
432 int i = row._row_start;
434 if (_text_string[i]._character != (
char32_t)text_soft_hyphen_key &&
435 _text_string[i]._character != (
char32_t)text_soft_break_key) {
440 if (_text_string[n - 1]._character != (
char32_t)text_soft_hyphen_key &&
441 _text_string[n - 1]._character != (
char32_t)text_soft_break_key) {
443 if (_text_string[n - 1]._character ==
'\n') {
444 is_real_char =
false;
447 is_real_char =
false;
452 c = min(n - row._row_start, (
int)row._string.size());
453 if (_text_string[n - 1]._character ==
'\n') {
454 is_real_char =
false;
471 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0);
472 if (r == (
int)_text_block.size()) {
474 return _text_string.size();
477 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0);
478 const TextRow &row = _text_block[r];
480 if (row._got_soft_hyphens) {
483 int n = row._row_start;
485 if (_text_string[n]._character != (
char32_t)text_soft_hyphen_key &&
486 _text_string[n]._character != (
char32_t)text_soft_break_key) {
495 return row._row_start + c;
510 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0.0f);
511 if (r == (
int)_text_block.size()) {
512 nassertr(c == 0, 0.0f);
516 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0.0f);
517 const TextRow &row = _text_block[r];
518 PN_stdfloat xpos = row._xpos;
519 for (
int i = 0; i < c; ++i) {
535 PlacedGlyphs placed_glyphs;
536 assemble_paragraph(placed_glyphs);
544 shadow_node->add_child(shadow_geom_node);
548 text_node->add_child(text_geom_node);
555 bool any_shadow =
false;
557 GeomCollectorMap geom_collector_map;
558 GeomCollectorMap geom_shadow_collector_map;
560 QuadMap quad_shadow_map;
562 PlacedGlyphs::const_iterator pgi;
563 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
564 const GlyphPlacement &placement = (*pgi);
566 if (placement._properties != properties) {
568 properties = placement._properties;
571 if (properties->has_shadow()) {
576 shadow_state.clear();
580 if (!placement._glyph.is_null()) {
581 if (properties->has_shadow()) {
582 if (_dynamic_merge) {
583 if (placement._glyph->has_quad()) {
584 placement.assign_quad_to(quad_shadow_map, shadow_state, shadow);
586 placement.assign_append_to(geom_shadow_collector_map, shadow_state, shadow);
589 placement.assign_to(shadow_geom_node, shadow_state, shadow);
599 if (_dynamic_merge) {
600 if (placement._glyph->has_quad()) {
601 placement.assign_quad_to(quad_map, text_state);
603 placement.assign_append_to(geom_collector_map, text_state);
606 placement.assign_to(text_geom_node, text_state);
609 placement.copy_graphic_to(text_node, text_state);
611 placed_glyphs.clear();
616 parent_node->add_child(shadow_node);
619 GeomCollectorMap::iterator gc;
620 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
621 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
624 generate_quads(text_geom_node, quad_map);
627 for (gc = geom_shadow_collector_map.begin();
628 gc != geom_shadow_collector_map.end();
630 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
633 generate_quads(shadow_geom_node, quad_shadow_map);
636 parent_node->add_child(text_node);
650 return calc_width((
char32_t)character, properties);
662 if (character ==
' ') {
665 nassertr(font !=
nullptr, 0.0f);
672 UnicodeLatinMap::AccentType accent_type;
673 int additional_flags;
674 PN_stdfloat glyph_scale;
675 PN_stdfloat advance_scale;
676 get_character_glyphs(character, &properties,
677 got_glyph, first_glyph, second_glyph, accent_type,
678 additional_flags, glyph_scale, advance_scale);
680 PN_stdfloat advance = 0.0f;
682 if (first_glyph !=
nullptr) {
683 advance = first_glyph->get_advance() * advance_scale;
685 if (second_glyph !=
nullptr) {
686 advance += second_glyph->get_advance();
691 return advance * glyph_scale;
717 if (character ==
' ' || character ==
'\n') {
724 nassertr(font !=
nullptr,
false);
727 return font->get_glyph(character, glyph);
741 if (character ==
' ' || character ==
'\n') {
750 UnicodeLatinMap::AccentType accent_type;
751 int additional_flags;
752 PN_stdfloat glyph_scale;
753 PN_stdfloat advance_scale;
754 get_character_glyphs(character, &properties,
755 got_glyph, first_glyph, second_glyph, accent_type,
756 additional_flags, glyph_scale, advance_scale);
776 if (character ==
' ' || character ==
'\n') {
783 nassertr(font !=
nullptr,
false);
786 if (!font->get_glyph(character, glyph)) {
790 return glyph->is_whitespace();
800 wstring::const_iterator &si,
801 const wstring::const_iterator &send,
802 TextAssembler::ComputedProperties *current_cprops) {
804 if ((*si) == (
wchar_t)text_push_properties_key) {
810 while (si != send && (*si) != (
wchar_t)text_push_properties_key) {
818 <<
"Unclosed push_properties in text.\n";
825 PT(ComputedProperties) new_cprops =
826 new ComputedProperties(current_cprops, wname, _encoder);
829 scan_wtext(output_string, si, send, new_cprops);
831 if (text_cat.is_debug()) {
837 <<
"push_properties not matched by pop_properties.\n";
841 }
else if ((*si) == (
wchar_t)text_pop_properties_key) {
847 }
else if ((*si) == (
wchar_t)text_embed_graphic_key) {
852 wstring graphic_wname;
854 while (si != send && (*si) != (
wchar_t)text_embed_graphic_key) {
855 graphic_wname += (*si);
862 <<
"Unclosed embed_graphic in text.\n";
870 std::string graphic_name = _encoder->
encode_wtext(graphic_wname);
877 if (named_graphic !=
nullptr) {
878 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
882 <<
"Unknown TextGraphic: " << graphic_name <<
"\n";
885#if WCHAR_MAX < 0x10FFFF
886 }
else if (*si >= 0xd800 && *si < 0xdc00) {
892 <<
"High surrogate at end of text.\n";
896 if (ch2 >= 0xdc00 && ch2 < 0xe000) {
897 char32_t code_point = 0x10000 + ((ch - 0xd800) << 10) + (ch2 - 0xdc00);
898 output_string.push_back(TextCharacter(code_point, current_cprops));
902 <<
"High surrogate was not followed by low surrogate in text.\n";
908 output_string.push_back(TextCharacter(*si, current_cprops));
930 if (_text_string.empty()) {
937 _text_block.push_back(TextRow(p));
940 PN_stdfloat initial_width = 0.0f;
941 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
942 if (_text_string[p]._character ==
'\n') {
943 initial_width = 0.0f;
944 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
948 _text_block.back()._eol_cprops = _text_string[p]._cprops;
949 _text_block.push_back(TextRow(p + 1));
952 _text_block.back()._string.push_back(_text_string[p]);
956 bool needs_newline =
false;
958 while (p < _text_string.size()) {
959 nassertr(!isspacew(_text_string[p]._character),
false);
965 bool any_spaces =
false;
966 size_t last_space = 0;
967 PN_stdfloat last_space_width = 0.0f;
969 bool any_hyphens =
false;
970 size_t last_hyphen = 0;
971 bool output_hyphen =
false;
973 bool overflow =
false;
974 PN_stdfloat wordwrap_width = -1.0f;
976 bool last_was_space =
false;
977 PN_stdfloat width = initial_width;
978 while (q < _text_string.size() && _text_string[q]._character !=
'\n') {
979 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
980 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
982 wordwrap_width = -1.0f;
985 if (isspacew(_text_string[q]._character) ||
986 _text_string[q]._character == (
char32_t)text_soft_break_key) {
987 if (!last_was_space) {
994 last_space_width = width;
995 last_was_space =
true;
998 last_was_space =
false;
1003 if (_text_string[q]._character == (
char32_t)text_soft_hyphen_key) {
1004 if (wordwrap_width > 0.0f) {
1008 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
1020 if (wordwrap_width > 0.0f && width > wordwrap_width) {
1032 nassertr(wordwrap_width > 0.0f,
false);
1034 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
1037 any_hyphens =
false;
1043 output_hyphen =
true;
1045 }
else if (any_spaces) {
1054 while ((
int)i < text_max_never_break && q - i > p &&
1055 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
1058 if ((
int)i < text_max_never_break) {
1065 size_t next_start = q;
1066 while (next_start < _text_string.size() &&
1067 isbreakpoint(_text_string[next_start]._character)) {
1072 while (q > p && isspacew(_text_string[q - 1]._character)) {
1076 if (next_start == p) {
1081 if (initial_width == 0.0f) {
1087 while (next_start < _text_string.size() &&
1088 isbreakpoint(_text_string[next_start]._character)) {
1094 if (needs_newline) {
1095 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1099 _text_block.push_back(TextRow(p));
1102 needs_newline =
true;
1105 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
1109 for (
size_t pi = p; pi < q; pi++) {
1110 if (_text_string[pi]._character != (
char32_t)text_soft_hyphen_key &&
1111 _text_string[pi]._character != (
char32_t)text_soft_break_key) {
1112 _text_block.back()._string.push_back(_text_string[pi]);
1114 _text_block.back()._got_soft_hyphens =
true;
1117 if (output_hyphen) {
1118 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1119 wstring::const_iterator wi;
1120 for (wi = text_soft_hyphen_output.begin();
1121 wi != text_soft_hyphen_output.end();
1123 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
1129 if (next_start < _text_string.size() && _text_string[next_start]._character ==
'\n') {
1131 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1135 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
1137 _text_block.push_back(TextRow(next_start));
1138 needs_newline =
false;
1143 initial_width = 0.0f;
1144 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
1145 if (_text_string[p]._character ==
'\n') {
1146 initial_width = 0.0f;
1147 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1151 _text_block.back()._eol_cprops = _text_string[p]._cprops;
1152 _text_block.push_back(TextRow(p + 1));
1154 initial_width +=
calc_width(_text_string[p]);
1155 _text_block.back()._string.push_back(_text_string[p]);
1168PN_stdfloat TextAssembler::
1169calc_hyphen_width(
const TextCharacter &tch) {
1170 TextFont *font = tch._cprops->_properties.get_font();
1171 nassertr(font !=
nullptr, 0.0f);
1173 PN_stdfloat hyphen_width = 0.0f;
1174 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1175 wstring::const_iterator wi;
1176 for (wi = text_soft_hyphen_output.begin();
1177 wi != text_soft_hyphen_output.end();
1179 hyphen_width +=
calc_width(*wi, tch._cprops->_properties);
1182 return hyphen_width;
1189generate_quads(
GeomNode *geom_node,
const QuadMap &quad_map) {
1190 QuadMap::const_iterator qmi;
1191 for (qmi = quad_map.begin(); qmi != quad_map.end(); ++qmi) {
1192 const QuadDefs &quads = qmi->second;
1194 glyphs.reserve(quads.size());
1206 vtx_handle->unclean_set_num_rows(quads.size() * 4);
1208 unsigned char *write_ptr = vtx_handle->get_write_pointer();
1212 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float32);
1214 PN_float32 *vtx_ptr = (PN_float32 *)
1215 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1216 PN_float32 *tex_ptr = (PN_float32 *)
1217 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1219 for (
const QuadDef &quad : quads) {
1220 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1222 vtx_ptr[2] = quad._dimensions[3];
1225 tex_ptr[0] = quad._uvs[0];
1226 tex_ptr[1] = quad._uvs[3];
1229 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1231 vtx_ptr[2] = quad._dimensions[1];
1234 tex_ptr[0] = quad._uvs[0];
1235 tex_ptr[1] = quad._uvs[1];
1238 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1240 vtx_ptr[2] = quad._dimensions[3];
1243 tex_ptr[0] = quad._uvs[2];
1244 tex_ptr[1] = quad._uvs[3];
1247 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1249 vtx_ptr[2] = quad._dimensions[1];
1252 tex_ptr[0] = quad._uvs[2];
1253 tex_ptr[1] = quad._uvs[1];
1256 glyphs.push_back(std::move(quad._glyph));
1260 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float64);
1262 PN_float64 *vtx_ptr = (PN_float64 *)
1263 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1264 PN_float64 *tex_ptr = (PN_float64 *)
1265 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1267 for (
const QuadDef &quad : quads) {
1268 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1270 vtx_ptr[2] = quad._dimensions[3];
1273 tex_ptr[0] = quad._uvs[0];
1274 tex_ptr[1] = quad._uvs[3];
1277 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1279 vtx_ptr[2] = quad._dimensions[1];
1282 tex_ptr[0] = quad._uvs[0];
1283 tex_ptr[1] = quad._uvs[1];
1286 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1288 vtx_ptr[2] = quad._dimensions[3];
1291 tex_ptr[0] = quad._uvs[2];
1292 tex_ptr[1] = quad._uvs[3];
1295 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1297 vtx_ptr[2] = quad._dimensions[1];
1300 tex_ptr[0] = quad._uvs[2];
1301 tex_ptr[1] = quad._uvs[1];
1304 glyphs.push_back(std::move(quad._glyph));
1310 int vtx_count = quads.size() * 4;
1312 if (vtx_count > 65535) {
1313 tris->set_index_type(GeomEnums::NT_uint32);
1315 tris->set_index_type(GeomEnums::NT_uint16);
1319 idx_handle->unclean_set_num_rows(quads.size() * 6);
1320 if (tris->get_index_type() == GeomEnums::NT_uint16) {
1322 uint16_t *idx_ptr = (uint16_t *)idx_handle->get_write_pointer();
1324 for (
int i = 0; i < vtx_count; i += 4) {
1325 *(idx_ptr++) = i + 0;
1326 *(idx_ptr++) = i + 1;
1327 *(idx_ptr++) = i + 2;
1328 *(idx_ptr++) = i + 2;
1329 *(idx_ptr++) = i + 1;
1330 *(idx_ptr++) = i + 3;
1334 uint32_t *idx_ptr = (uint32_t *)idx_handle->get_write_pointer();
1336 for (
int i = 0; i < vtx_count; i += 4) {
1337 *(idx_ptr++) = i + 0;
1338 *(idx_ptr++) = i + 1;
1339 *(idx_ptr++) = i + 2;
1340 *(idx_ptr++) = i + 2;
1341 *(idx_ptr++) = i + 1;
1342 *(idx_ptr++) = i + 3;
1348 tris->set_minmax(0, vtx_count - 1,
nullptr,
nullptr);
1351 geom->_glyphs.swap(glyphs);
1352 geom->add_primitive(tris);
1353 geom_node->
add_geom(geom, qmi->first);
1363 _ul.set(0.0f, 0.0f);
1364 _lr.set(0.0f, 0.0f);
1367 PN_stdfloat ypos = 0.0f;
1368 _next_row_ypos = 0.0f;
1369 TextBlock::iterator bi;
1370 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
1371 TextRow &row = (*bi);
1374 size_t first_glyph = placed_glyphs.size();
1377 PN_stdfloat row_width, line_height, wordwrap;
1378 TextProperties::Alignment align;
1379 assemble_row(row, placed_glyphs,
1380 row_width, line_height, align, wordwrap);
1384 if (num_rows == 0) {
1386 _ul[1] = 0.8f * line_height;
1391 ypos -= line_height;
1393 _lr[1] = ypos - 0.2 * line_height;
1400 PN_stdfloat xpos = 0;
1402 case TextProperties::A_left:
1403 _lr[0] = max(_lr[0], row_width);
1406 case TextProperties::A_right:
1408 _ul[0] = min(_ul[0], xpos);
1411 case TextProperties::A_center:
1412 xpos = -0.5f * row_width;
1413 _ul[0] = min(_ul[0], xpos);
1414 _lr[0] = max(_lr[0], -xpos);
1417 case TextProperties::A_boxed_left:
1418 _lr[0] = max(_lr[0], max(row_width, wordwrap));
1421 case TextProperties::A_boxed_right:
1422 xpos = wordwrap - row_width;
1423 _ul[0] = min(_ul[0], xpos);
1426 case TextProperties::A_boxed_center:
1427 xpos = -0.5f * row_width;
1428 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
1429 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
1430 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
1438 for (
size_t i = first_glyph; i < placed_glyphs.size(); ++i) {
1439 placed_glyphs[i]._xpos += xpos;
1440 placed_glyphs[i]._ypos += ypos;
1445 _next_row_ypos = ypos - line_height;
1460assemble_row(TextAssembler::TextRow &row,
1462 PN_stdfloat &row_width, PN_stdfloat &line_height,
1463 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
1467 PN_stdfloat xpos = 0.0f;
1468 align = TextProperties::A_left;
1473 bool underscore =
false;
1474 PN_stdfloat underscore_start = 0.0f;
1478 const ComputedProperties *prev_cprops =
nullptr;
1479 hb_buffer_t *harfbuff =
nullptr;
1482 for (
const TextCharacter &tch : row._string) {
1483 char32_t character = tch._character;
1488 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
1491 if (underscore && underscore_start != xpos) {
1492 draw_underscore(placed_glyphs, underscore_start, xpos,
1493 underscore_properties);
1496 underscore_start = xpos;
1497 underscore_properties = properties;
1501 nassertv(font !=
nullptr);
1504 if ((align == TextProperties::A_left) &&
1505 (properties->get_align() != TextProperties::A_left)) {
1506 align = properties->get_align();
1510 if (properties->get_wordwrap() > 0.0f) {
1511 wordwrap = properties->get_wordwrap();
1516 if (graphic !=
nullptr) {
1518 line_height = max(line_height, frame[3] - frame[2]);
1524 if (tch._cprops != prev_cprops || graphic !=
nullptr) {
1525 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1527 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1528 hb_buffer_reset(harfbuff);
1530 }
else if (harfbuff ==
nullptr && text_use_harfbuzz &&
1531 font->
is_of_type(DynamicTextFont::get_class_type())) {
1532 harfbuff = hb_buffer_create();
1534 prev_cprops = tch._cprops;
1537 if (graphic ==
nullptr && harfbuff !=
nullptr) {
1538 unsigned int cluster = character;
1543 if (map_entry !=
nullptr &&
1544 map_entry->_toupper_character != (
char32_t)character) {
1545 character = map_entry->_toupper_character;
1548 cluster |= CF_small_caps;
1551 hb_buffer_add(harfbuff, character, cluster);
1556 if (character ==
' ') {
1561 }
else if (character ==
'\t') {
1564 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
1567 }
else if (character == (
char32_t)text_soft_hyphen_key) {
1570 }
else if (graphic !=
nullptr) {
1572 GlyphPlacement placement;
1579 model_node->set_preserve_transform(ModelNode::PT_no_touch);
1580 model_node->add_child(model);
1581 placement._graphic_model = model_node.p();
1585 placement._graphic_model = model->copy_subgraph();
1591 PN_stdfloat advance = (frame[1] - frame[0]);
1596 placement._xpos = (xpos - frame[0]);
1598 placement._slant = properties->
get_slant();
1599 placement._properties = properties;
1601 placed_glyphs.push_back(placement);
1603 xpos += advance * glyph_scale;
1611 UnicodeLatinMap::AccentType accent_type;
1612 int additional_flags;
1613 PN_stdfloat glyph_scale;
1614 PN_stdfloat advance_scale;
1615 get_character_glyphs(character, properties,
1616 got_glyph, first_glyph, second_glyph, accent_type,
1617 additional_flags, glyph_scale, advance_scale);
1621 sprintf(buffer,
"U+%04x", character);
1623 <<
"No definition in " << font->get_name()
1624 <<
" for character " << buffer;
1625 if (character < 128 && isprint((
unsigned int)character)) {
1626 text_cat.warning(
false)
1627 <<
" ('" << (char)character <<
"')";
1629 text_cat.warning(
false)
1637 if (prev_char != -1) {
1638 xpos += font->
get_kerning(prev_char, character) * glyph_scale;
1640 prev_char = character;
1647 GlyphPlacement placement;
1649 placement._glyph =
nullptr;
1650 placement._scale = glyph_scale;
1651 placement._xpos = xpos;
1653 placement._slant = properties->
get_slant();
1654 placement._properties = properties;
1656 PN_stdfloat advance = 0.0f;
1658 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
1661 LPoint3 min_vert, max_vert;
1662 bool found_any =
false;
1663 if (first_glyph !=
nullptr) {
1664 first_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1667 if (second_glyph !=
nullptr) {
1668 second_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1673 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
1675 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
1686 placement._scale *= -1;
1687 placement._xpos += centroid[0] * 2;
1688 placement._ypos += centroid[2] * 2;
1691 if (accent_type != UnicodeLatinMap::AT_none) {
1692 GlyphPlacement accent_placement(placement);
1693 tack_on_accent(accent_type, min_vert, max_vert, centroid,
1694 properties, accent_placement);
1695 placed_glyphs.push_back(accent_placement);
1700 if (first_glyph !=
nullptr) {
1701 advance = first_glyph->get_advance() * advance_scale;
1702 if (!first_glyph->is_whitespace()) {
1703 std::swap(placement._glyph, first_glyph);
1704 placed_glyphs.push_back(placement);
1710 if (second_glyph !=
nullptr) {
1711 placement._xpos += advance * glyph_scale;
1712 advance += second_glyph->get_advance();
1713 std::swap(placement._glyph, second_glyph);
1714 placed_glyphs.push_back(placement);
1717 xpos += advance * glyph_scale;
1722 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1723 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1725 hb_buffer_destroy(harfbuff);
1728 if (underscore && underscore_start != xpos) {
1729 draw_underscore(placed_glyphs, underscore_start, xpos,
1730 underscore_properties);
1735 if (row._eol_cprops !=
nullptr) {
1740 const TextProperties *properties = &(row._eol_cprops->_properties);
1742 nassertv(font !=
nullptr);
1744 if (line_height == 0.0f) {
1746 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1755shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos,
1761 hb_direction_t direction = HB_DIRECTION_INVALID;
1764 case TextProperties::D_ltr:
1765 direction = HB_DIRECTION_LTR;
1767 case TextProperties::D_rtl:
1768 direction = HB_DIRECTION_RTL;
1772 hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
1773 hb_buffer_set_direction(buf, direction);
1774 hb_buffer_guess_segment_properties(buf);
1776 DynamicTextFont *font = DCAST(DynamicTextFont, properties.
get_font());
1777 hb_font_t *hb_font = font->get_hb_font();
1778 hb_shape(hb_font, buf,
nullptr, 0);
1781 PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0);
1783 unsigned int glyph_count;
1784 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
1785 hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
1787 for (
unsigned int i = 0; i < glyph_count; ++i) {
1788 unsigned int cluster = glyph_info[i].cluster;
1789 int character = cluster & 0x1fffff;
1790 int glyph_index = glyph_info[i].codepoint;
1793 if (!font->get_glyph_by_index(character, glyph_index, glyph)) {
1795 sprintf(buffer,
"U+%04x", character);
1797 <<
"No definition in " << font->get_name()
1798 <<
" for character " << buffer;
1799 if (character < 128 && isprint((
unsigned int)character)) {
1800 text_cat.warning(
false)
1801 <<
" ('" << (char)character <<
"')";
1803 text_cat.warning(
false)
1807 PN_stdfloat advance = glyph_pos[i].x_advance * scale;
1808 if (glyph->is_whitespace()) {
1814 PN_stdfloat x_offset = glyph_pos[i].x_offset * scale;
1815 PN_stdfloat y_offset = glyph_pos[i].y_offset * scale;
1821 GlyphPlacement placement;
1822 placement._glyph = std::move(glyph);
1823 placement._scale = glyph_scale;
1824 placement._xpos = xpos + x_offset;
1826 placement._slant = properties.
get_slant();
1827 placement._properties = &properties;
1829 if (cluster & CF_small_caps) {
1834 placed_glyphs.push_back(placement);
1847 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
1853 vdata->unclean_set_num_rows(2);
1858 vertex.set_data3(underscore_start, 0.0f, y);
1859 color.set_data4(underscore_properties->get_text_color());
1860 vertex.set_data3(underscore_end, 0.0f, y);
1861 color.set_data4(underscore_properties->get_text_color());
1864 lines->add_next_vertices(2);
1865 lines->close_primitive();
1868 geom->add_primitive(lines);
1877 GlyphPlacement placement;
1878 placement._glyph = std::move(glyph);
1879 placement._xpos = 0;
1880 placement._ypos = 0;
1881 placement._scale = 1;
1882 placement._slant = 0;
1883 placement._properties = underscore_properties;
1884 placed_glyphs.push_back(placement);
1900get_character_glyphs(
int character,
const TextProperties *properties,
1903 UnicodeLatinMap::AccentType &accent_type,
1904 int &additional_flags,
1905 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
1907 nassertv_always(font !=
nullptr);
1911 second_glyph =
nullptr;
1912 accent_type = UnicodeLatinMap::AT_none;
1913 additional_flags = 0;
1915 advance_scale = 1.0f;
1921 if (map_entry !=
nullptr) {
1923 map_entry->_toupper_character != (
char32_t)character) {
1924 character = map_entry->_toupper_character;
1930 got_glyph = font->get_glyph(character, glyph);
1931 if (!got_glyph && map_entry !=
nullptr && map_entry->_ascii_equiv != 0) {
1934 if (map_entry->_ascii_equiv ==
'i') {
1936 got_glyph = font->get_glyph(0x0131, glyph) ||
1937 font->get_glyph(
'i', glyph);
1939 }
else if (map_entry->_ascii_equiv ==
'j') {
1941 got_glyph = font->get_glyph(0x0237, glyph) ||
1942 font->get_glyph(
'j', glyph);
1945 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1948 if (!got_glyph && map_entry->_toupper_character != (
char32_t)character) {
1950 character = map_entry->_toupper_character;
1952 if (map_entry !=
nullptr) {
1953 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1958 accent_type = map_entry->_accent_type;
1959 additional_flags = map_entry->_additional_flags;
1961 bool got_second_glyph =
false;
1962 if (map_entry->_ascii_additional != 0) {
1965 font->get_glyph(map_entry->_ascii_additional, second_glyph);
1968 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
1973 advance_scale = ligature_advance_scale;
1976 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
1989tack_on_accent(UnicodeLatinMap::AccentType accent_type,
1990 const LPoint3 &min_vert,
const LPoint3 &max_vert,
1991 const LPoint3 ¢roid,
1993 TextAssembler::GlyphPlacement &placement)
const {
1997 if (combine_char != 0 &&
1998 tack_on_accent(combine_char, CP_above, CT_none, min_vert, max_vert,
1999 centroid, properties, placement)) {
2004 switch (accent_type) {
2005 case UnicodeLatinMap::AT_grave:
2010 tack_on_accent(
'/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
2011 properties, placement);
2014 case UnicodeLatinMap::AT_acute:
2015 tack_on_accent(
'/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
2016 properties, placement);
2019 case UnicodeLatinMap::AT_breve:
2020 tack_on_accent(
')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
2021 properties, placement);
2024 case UnicodeLatinMap::AT_inverted_breve:
2025 tack_on_accent(
'(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
2026 properties, placement);
2029 case UnicodeLatinMap::AT_circumflex:
2030 tack_on_accent(
'^', CP_above, CT_none, min_vert, max_vert, centroid,
2031 properties, placement) ||
2032 tack_on_accent(
'v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
2033 properties, placement);
2036 case UnicodeLatinMap::AT_circumflex_below:
2037 tack_on_accent(
'^', CP_below, CT_none, min_vert, max_vert, centroid,
2038 properties, placement) ||
2039 tack_on_accent(
'v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
2040 properties, placement);
2043 case UnicodeLatinMap::AT_caron:
2044 tack_on_accent(
'^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
2045 properties, placement) ||
2046 tack_on_accent(
'v', CP_above, CT_squash, min_vert, max_vert, centroid,
2047 properties, placement);
2051 case UnicodeLatinMap::AT_tilde:
2052 tack_on_accent(
'~', CP_above, CT_none, min_vert, max_vert, centroid,
2053 properties, placement) ||
2054 tack_on_accent(
's', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
2055 properties, placement);
2059 case UnicodeLatinMap::AT_tilde_below:
2060 tack_on_accent(
'~', CP_below, CT_none, min_vert, max_vert, centroid,
2061 properties, placement) ||
2062 tack_on_accent(
's', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
2063 properties, placement);
2066 case UnicodeLatinMap::AT_diaeresis:
2067 tack_on_accent(
':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
2068 properties, placement);
2071 case UnicodeLatinMap::AT_diaeresis_below:
2072 tack_on_accent(
':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
2073 properties, placement);
2076 case UnicodeLatinMap::AT_dot_above:
2077 tack_on_accent(
'.', CP_above, CT_none, min_vert, max_vert, centroid,
2078 properties, placement);
2081 case UnicodeLatinMap::AT_dot_below:
2082 tack_on_accent(
'.', CP_below, CT_none, min_vert, max_vert, centroid,
2083 properties, placement);
2086 case UnicodeLatinMap::AT_macron:
2087 tack_on_accent(
'-', CP_above, CT_none, min_vert, max_vert, centroid,
2088 properties, placement);
2091 case UnicodeLatinMap::AT_line_below:
2092 tack_on_accent(
'-', CP_below, CT_none, min_vert, max_vert, centroid,
2093 properties, placement);
2096 case UnicodeLatinMap::AT_ring_above:
2097 tack_on_accent(
'o', CP_top, CT_tiny, min_vert, max_vert, centroid,
2098 properties, placement);
2101 case UnicodeLatinMap::AT_ring_below:
2102 tack_on_accent(
'o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
2103 properties, placement);
2106 case UnicodeLatinMap::AT_cedilla:
2107 tack_on_accent(0xb8, CP_below, CT_none, min_vert, max_vert, centroid,
2108 properties, placement) ||
2109 tack_on_accent(
'c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
2110 properties, placement);
2115 case UnicodeLatinMap::AT_comma_below:
2116 tack_on_accent(
',', CP_below, CT_none, min_vert, max_vert, centroid,
2117 properties, placement);
2120 case UnicodeLatinMap::AT_ogonek:
2121 tack_on_accent(
',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
2122 properties, placement);
2125 case UnicodeLatinMap::AT_stroke:
2126 tack_on_accent(
'/', CP_within, CT_none, min_vert, max_vert, centroid,
2127 properties, placement);
2142tack_on_accent(
wchar_t accent_mark, TextAssembler::CheesyPosition position,
2143 TextAssembler::CheesyTransform transform,
2144 const LPoint3 &min_vert,
const LPoint3 &max_vert,
2145 const LPoint3 ¢roid,
2147 TextAssembler::GlyphPlacement &placement)
const {
2149 nassertr(font !=
nullptr,
false);
2154 if (font->get_glyph(accent_mark, accent_glyph) ||
2155 font->get_glyph(toupper(accent_mark), accent_glyph)) {
2156 if (!accent_glyph->is_whitespace()) {
2157 LPoint3 min_accent, max_accent;
2158 bool found_any =
false;
2159 accent_glyph->calc_tight_bounds(min_accent, max_accent, found_any,
2163 LMatrix4 accent_mat;
2164 bool has_mat =
true;
2166 switch (transform) {
2172 accent_mat = LMatrix4::scale_mat(-1.0f, -1.0f, 1.0f);
2174 min_accent[0] = -max_accent[0];
2179 accent_mat = LMatrix4::scale_mat(1.0f, -1.0f, -1.0f);
2181 min_accent[2] = -max_accent[2];
2186 accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2190 max_accent[0] = -min_accent[2];
2191 min_accent[0] = -max_accent[2];
2198 placement._scale *= -1;
2200 min_accent[0] = -max_accent[0];
2203 min_accent[2] = -max_accent[2];
2208 accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2212 min_accent[0] = min_accent[2];
2213 max_accent[0] = max_accent[2];
2219 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
2220 min_accent[0] *= squash_accent_scale_x;
2221 max_accent[0] *= squash_accent_scale_x;
2222 min_accent[2] *= squash_accent_scale_y;
2223 max_accent[2] *= squash_accent_scale_y;
2226 case CT_squash_mirror_y:
2227 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, -1.0f, -squash_accent_scale_y);
2228 min_accent[0] *= squash_accent_scale_x;
2229 max_accent[0] *= squash_accent_scale_x;
2231 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
2232 max_accent[2] = -t * squash_accent_scale_y;
2235 case CT_squash_mirror_diag:
2237 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2238 LMatrix4::scale_mat(-squash_accent_scale_x, -1.0f, squash_accent_scale_y);
2243 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
2244 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
2245 min_accent[2] = -u * squash_accent_scale_y;
2246 max_accent[2] = -t * squash_accent_scale_y;
2249 case CT_small_squash:
2250 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
2251 min_accent[0] *= small_squash_accent_scale_x;
2252 max_accent[0] *= small_squash_accent_scale_x;
2253 min_accent[2] *= small_squash_accent_scale_y;
2254 max_accent[2] *= small_squash_accent_scale_y;
2257 case CT_small_squash_mirror_y:
2258 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, -1.0f, -small_squash_accent_scale_y);
2259 min_accent[0] *= small_squash_accent_scale_x;
2260 max_accent[0] *= small_squash_accent_scale_x;
2262 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
2263 max_accent[2] = -t * small_squash_accent_scale_y;
2266 case CT_small_squash_mirror_diag:
2268 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2269 LMatrix4::scale_mat(-small_squash_accent_scale_x, -1.0f, small_squash_accent_scale_y);
2274 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
2275 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
2276 min_accent[2] = -u * small_squash_accent_scale_y;
2277 max_accent[2] = -t * small_squash_accent_scale_y;
2282 placement._scale *= small_accent_scale;
2283 min_accent *= small_accent_scale;
2284 max_accent *= small_accent_scale;
2287 case CT_small_rotate_270:
2289 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2290 LMatrix4::scale_mat(small_accent_scale);
2295 min_accent[0] = min_accent[2] * small_accent_scale;
2296 max_accent[0] = max_accent[2] * small_accent_scale;
2297 min_accent[2] = -u * small_accent_scale;
2298 max_accent[2] = -t * small_accent_scale;
2303 placement._scale *= tiny_accent_scale;
2304 min_accent *= tiny_accent_scale;
2305 max_accent *= tiny_accent_scale;
2308 case CT_tiny_mirror_x:
2309 accent_mat = LMatrix4::scale_mat(-tiny_accent_scale, -1.0f, tiny_accent_scale);
2312 min_accent[0] = -max_accent[0] * tiny_accent_scale;
2313 max_accent[0] = -t * tiny_accent_scale;
2314 min_accent[2] *= tiny_accent_scale;
2315 max_accent[2] *= tiny_accent_scale;
2318 case CT_tiny_rotate_270:
2320 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2321 LMatrix4::scale_mat(tiny_accent_scale);
2326 min_accent[0] = min_accent[2] * tiny_accent_scale;
2327 max_accent[0] = max_accent[2] * tiny_accent_scale;
2328 min_accent[2] = -u * tiny_accent_scale;
2329 max_accent[2] = -t * tiny_accent_scale;
2338 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
2339 PN_stdfloat accent_height = max_accent[2] - min_accent[2] - total_margin * 2;
2340 PN_stdfloat accent_x = centroid[0] - accent_centroid[0];
2341 PN_stdfloat accent_y = 0;
2342 PN_stdfloat min_y = min_vert[2] + total_margin;
2343 PN_stdfloat max_y = max_vert[2] - total_margin;
2348 accent_y = max_y - accent_centroid[2] + accent_height * 0.75f;
2353 accent_y = min_y - accent_centroid[2] - accent_height * 0.75f;
2358 accent_y = max_y - accent_centroid[2];
2363 accent_y = min_y - accent_centroid[2];
2368 accent_y = centroid[2] - accent_centroid[2];
2372 placement._xpos += placement._scale * (accent_x + placement._slant * accent_y);
2373 placement._ypos += placement._scale * accent_y;
2377 PT(
Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
2378 accent_geom->transform_vertices(accent_mat);
2379 placement._glyph =
new TextGlyph(0, accent_geom, accent_glyph->get_state(), 0);
2382 placement._glyph = accent_glyph;
2396void TextAssembler::ComputedProperties::
2397append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
2398 if (
this != other) {
2399 if (_depth > other->_depth) {
2401 nassertv(_based_on !=
nullptr);
2403 wtext.push_back(text_pop_properties_key);
2404 _based_on->append_delta(wtext, other);
2406 }
else if (other->_depth > _depth) {
2408 nassertv(other->_based_on !=
nullptr);
2410 append_delta(wtext, other->_based_on);
2411 wtext.push_back(text_push_properties_key);
2412 wtext += other->_wname;
2413 wtext.push_back(text_push_properties_key);
2415 }
else if (_depth != 0) {
2417 nassertv(_based_on !=
nullptr && other->_based_on !=
nullptr);
2419 wtext.push_back(text_pop_properties_key);
2420 _based_on->append_delta(wtext, other->_based_on);
2421 wtext.push_back(text_push_properties_key);
2422 wtext += other->_wname;
2423 wtext.push_back(text_push_properties_key);
2432void TextAssembler::GlyphPlacement::
2434 const LVector2 &offset)
const {
2436 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2437 0.0f, 1.0f, 0.0f, 0.0f,
2438 _slant * _scale, 0.0f, _scale, 0.0f,
2439 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2441 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2442 geom->transform_vertices(xform);
2443 geom_node->
add_geom(geom, state->compose(_glyph->get_state()));
2451void TextAssembler::GlyphPlacement::
2452assign_append_to(GeomCollectorMap &geom_collector_map,
2454 const LVector2 &offset)
const {
2456 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2457 0.0f, 1.0f, 0.0f, 0.0f,
2458 _slant * _scale, 0.0f, _scale, 0.0f,
2459 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2461 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2466 CPT(
RenderState) rs = _glyph->get_state()->compose(state);
2467 GeomCollectorKey key(rs, vdata->
get_format());
2469 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
2470 if (mi == geom_collector_map.end()) {
2471 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->
get_format()))).first;
2473 GeomCollector &geom_collector = (*mi).second;
2474 geom_collector.count_geom(geom);
2478 VertexIndexMap vimap;
2480 for (
size_t p = 0; p < geom->get_num_primitives(); ++p) {
2481 CPT(
GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
2484 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
2487 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
2489 e = primitive->get_primitive_end(sp);
2492 for (i = s; i < e; i++) {
2493 int vi = primitive->get_vertex(i);
2496 std::pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
2501 new_vertex = geom_collector.append_vertex(vdata, vi, xform);
2503 (*(added.first)).second = new_vertex;
2509 new_vertex = (*(added.first)).second;
2522void TextAssembler::GlyphPlacement::
2523assign_quad_to(QuadMap &quad_map,
const RenderState *state,
2524 const LVector2 &offset)
const {
2527 if (_glyph->get_quad(quad._dimensions, quad._uvs)) {
2528 quad._dimensions *= _scale;
2529 quad._slantl = quad._dimensions[1] * _slant;
2530 quad._slanth = quad._dimensions[3] * _slant;
2531 quad._dimensions += LVecBase4(_xpos, _ypos, _xpos, _ypos);
2532 quad._dimensions += LVecBase4(offset[0], -offset[1], offset[0], -offset[1]);
2533 quad._glyph = _glyph;
2535 quad_map[state->compose(_glyph->get_state())].push_back(std::move(quad));
2543void TextAssembler::GlyphPlacement::
2545 if (_graphic_model !=
nullptr) {
2548 node->add_child(intermediate_node);
2550 intermediate_node->set_transform(
2551 TransformState::make_pos_hpr_scale_shear(
2552 LVecBase3(_xpos, 0, _ypos),
2554 LVecBase3(_scale, 1, _scale),
2555 LVecBase3(0, _slant, 0)
2558 intermediate_node->set_state(state);
2559 intermediate_node->add_child(_graphic_model);
2567TextAssembler::GeomCollector::
2577TextAssembler::GeomCollector::
2578GeomCollector(
const TextAssembler::GeomCollector ©) :
2579 _vdata(copy._vdata),
2591 if (prim_type == GeomTriangles::get_class_type()) {
2592 if (_triangles ==
nullptr) {
2594 _geom->add_primitive(_triangles);
2598 }
else if (prim_type == GeomLines::get_class_type()) {
2599 if (_lines ==
nullptr) {
2600 _lines =
new GeomLines(Geom::UH_static);
2601 _geom->add_primitive(_lines);
2605 }
else if (prim_type == GeomPoints::get_class_type()) {
2606 if (_points ==
nullptr) {
2608 _geom->add_primitive(_points);
2613 nassert_raise(
"unexpected primitive type");
2621int TextAssembler::GeomCollector::
2623 const LMatrix4 &xform) {
2624 int new_row = _vdata->get_num_rows();
2628 vertex_rewriter.set_row_unsafe(new_row);
2629 LPoint3 point = vertex_rewriter.get_data3();
2630 vertex_rewriter.set_data3(point * xform);
2639void TextAssembler::GeomCollector::
2641 if (_geom->get_num_primitives() > 0) {
Defines a series of disconnected line segments.
A node that holds Geom objects, renderable pieces of geometry.
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Defines a series of disconnected points.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
bool close_primitive()
Indicates that the previous n calls to add_vertex(), since the last call to close_primitive(),...
int get_primitive_start(int n) const
Returns the element within the _vertices list at which the nth primitive starts.
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type.
This is a specialization on Geom for containing a primitive intended to represent a TextGlyph.
Defines a series of disconnected triangles.
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
A basic node of the scene graph or data graph.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
This class is not normally used directly by user code, but is used by the TextNode to lay out a block...
bool set_wtext(const std::wstring &wtext)
Accepts a new text string and associated properties structure, and precomputes the wordwrapping layou...
get_multiline_mode
Returns the multline_mode flag.
void clear()
Reinitializes the contents of the TextAssembler.
bool calc_r_c(int &r, int &c, int n) const
Computes the row and column index of the nth character or graphic object in the text.
static bool is_whitespace(wchar_t character, const TextProperties &properties)
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
std::wstring get_wordwrapped_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
std::wstring get_wtext() const
Returns a wstring that represents the contents of the text.
std::wstring get_wordwrapped_plain_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
PN_stdfloat get_xpos(int r, int c) const
Returns the x position of the origin of the character or graphic object at the indicated position in ...
int calc_index(int r, int c) const
Computes the character index of the character at the rth row and cth column position.
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
static bool has_exact_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font exactly as named, false otherwise.
static bool has_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font or can be synthesized by Panda,...
std::wstring get_plain_wtext() const
Returns a wstring that represents the contents of the text, without any embedded properties character...
bool set_wsubstr(const std::wstring &wtext, int start, int count)
Replaces the 'count' characters from 'start' of the current text with the indicated replacement text.
This class can be used to convert text between multiple representations, e.g.
std::string encode_wtext(const std::wstring &wtext) const
Encodes a wide-text string into a single-char string, according to the current encoding.
An encapsulation of a font; i.e.
PN_stdfloat get_total_poly_margin() const
Returns the total margin between the edge of the glyph and the edge of the cards.
get_line_height
Returns the number of units high each line of text is.
virtual PN_stdfloat get_kerning(int first, int second) const
Returns the amount by which to offset the second glyph when it directly follows the first glyph.
get_space_advance
Returns the number of units wide a space is.
A representation of a single glyph (character) from a font.
This defines a special model that has been constructed for the purposes of embedding an arbitrary gra...
get_frame
Returns the frame specified for the graphic.
get_model
Returns the NodePath associated with the graphic, that renders the desired image.
get_instance_flag
Returns the instance_flag.
This defines all of the TextProperties structures that might be referenced by name from an embedded t...
static TextPropertiesManager * get_global_ptr()
Returns the pointer to the global TextPropertiesManager object.
const TextGraphic * get_graphic_ptr(const std::string &name)
Returns a pointer to the TextGraphic with the indicated name, or NULL if there is no graphic with tha...
This defines the set of visual properties that may be assigned to the individual characters of the te...
const RenderState * get_shadow_state() const
Returns a RenderState object suitable for rendering the shadow of this text with these properties.
get_slant
Returns the factor by which the text is specified to slant to the right.
get_underscore_height
Returns the vertical height of the underscore; see set_underscore_height().
get_small_caps
Returns the small_caps flag.
get_glyph_shift
Returns the vertical shift of each letter as specified by set_glyph_shift().
const RenderState * get_text_state() const
Returns a RenderState object suitable for rendering text with these properties.
get_glyph_scale
Returns the scale factor of each letter as specified by set_glyph_scale().
get_shadow
Returns the offset of the shadow as set by set_shadow().
get_font
Returns the font currently in use, if any.
get_small_caps_scale
Returns the scale factor applied to lowercase letters from their uppercase equivalents,...
get_underscore
Returns the underscore flag.
get_direction
Returns the direction of the text as specified by set_direction().
get_tab_width
Returns the width set via set_tab_width().
get_text_scale
Returns the scale factor of the text as specified by set_text_scale().
A thread; that is, a lightweight process.
get_current_thread
Returns a pointer to the currently-executing Thread object.
TypeHandle is the identifier used to differentiate C++ class types.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
This class mainly serves as a container for a largish table of the subset of the Unicode character se...
static wchar_t get_combining_accent(AccentType accent)
Returns the unicode code point for the combining character corresponding with the given accent type,...
static const Entry * look_up(char32_t character)
Returns the Entry associated with the indicated character, if there is one.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.