48static const PN_stdfloat small_accent_scale = 0.6f;
51static const PN_stdfloat tiny_accent_scale = 0.4;
54static const PN_stdfloat squash_accent_scale_x = 0.8f;
55static const PN_stdfloat squash_accent_scale_y = 0.5f;
59static const PN_stdfloat small_squash_accent_scale_x = 0.6f;
60static const PN_stdfloat small_squash_accent_scale_y = 0.3;
64static const PN_stdfloat ligature_advance_scale = 0.6f;
72isspacew(
unsigned int ch) {
73 return isascii(ch) && isspace(ch);
81isbreakpoint(
unsigned int ch) {
82 return (ch ==
' ' || ch ==
'\t' ||
83 ch == (
unsigned int)text_soft_hyphen_key ||
84 ch == (
unsigned int)text_soft_break_key);
94 _usage_hint(
Geom::UH_static),
96 _dynamic_merge(text_dynamic_merge),
108 _initial_cprops(copy._initial_cprops),
109 _text_string(copy._text_string),
110 _text_block(copy._text_block),
113 _next_row_ypos(copy._next_row_ypos),
114 _encoder(copy._encoder),
115 _usage_hint(copy._usage_hint),
116 _max_rows(copy._max_rows),
117 _dynamic_merge(copy._dynamic_merge),
118 _multiline_mode(copy._multiline_mode)
127 _initial_cprops = copy._initial_cprops;
128 _text_string = copy._text_string;
129 _text_block = copy._text_block;
132 _next_row_ypos = copy._next_row_ypos;
133 _encoder = copy._encoder;
134 _usage_hint = copy._usage_hint;
135 _max_rows = copy._max_rows;
136 _dynamic_merge = copy._dynamic_merge;
137 _multiline_mode = copy._multiline_mode;
154 _next_row_ypos = 0.0f;
156 _text_string.clear();
174 wstring::const_iterator si = wtext.begin();
175 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
177 while (si != wtext.end()) {
182 <<
"pop_properties encountered without preceding push_properties.\n";
183 scan_wtext(_text_string, si, wtext.end(), _initial_cprops);
187 return wordwrap_text();
204set_wsubstr(
const wstring &wtext,
int start,
int count) {
205 nassertr(start >= 0 && start <= (
int)_text_string.size(),
false);
206 nassertr(count >= 0 && start + count <= (
int)_text_string.size(),
false);
211 wstring::const_iterator si = wtext.begin();
212 scan_wtext(substr, si, wtext.end(), _initial_cprops);
213 while (si != wtext.end()) {
215 <<
"pop_properties encountered without preceding push_properties.\n";
216 scan_wtext(substr, si, wtext.end(), _initial_cprops);
219 _text_string.erase(_text_string.begin() + start, _text_string.begin() + start + count);
220 _text_string.insert(_text_string.begin() + start, substr.begin(), substr.end());
222 return wordwrap_text();
238 TextString::const_iterator si;
239 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
240 const TextCharacter &tch = (*si);
241 if (tch._graphic ==
nullptr) {
242 wtext += tch._character;
265 TextBlock::const_iterator bi;
266 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
267 const TextRow &row = (*bi);
268 if (bi != _text_block.begin()) {
272 TextString::const_iterator si;
273 for (si = row._string.begin(); si != row._string.end(); ++si) {
274 const TextCharacter &tch = (*si);
275 if (tch._graphic ==
nullptr) {
276 wtext += tch._character;
296 PT(ComputedProperties) current_cprops = _initial_cprops;
298 TextString::const_iterator si;
299 for (si = _text_string.begin(); si != _text_string.end(); ++si) {
300 const TextCharacter &tch = (*si);
301 current_cprops->append_delta(wtext, tch._cprops);
302 if (tch._graphic ==
nullptr) {
303 wtext += tch._character;
305 wtext.push_back(text_embed_graphic_key);
306 wtext += tch._graphic_wname;
307 wtext.push_back(text_embed_graphic_key);
309 current_cprops = tch._cprops;
311 current_cprops->append_delta(wtext, _initial_cprops);
333 PT(ComputedProperties) current_cprops = _initial_cprops;
335 TextBlock::const_iterator bi;
336 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
337 const TextRow &row = (*bi);
338 if (bi != _text_block.begin()) {
339 current_cprops->append_delta(wtext, _initial_cprops);
340 current_cprops = _initial_cprops;
344 TextString::const_iterator si;
345 for (si = row._string.begin(); si != row._string.end(); ++si) {
346 const TextCharacter &tch = (*si);
347 current_cprops->append_delta(wtext, tch._cprops);
348 if (tch._graphic ==
nullptr) {
349 wtext += tch._character;
351 wtext.push_back(text_embed_graphic_key);
352 wtext += tch._graphic_wname;
353 wtext.push_back(text_embed_graphic_key);
355 current_cprops = tch._cprops;
358 current_cprops->append_delta(wtext, _initial_cprops);
373calc_r_c(
int &r,
int &c,
int n)
const {
374 nassertr(n >= 0 && n <= (
int)_text_string.size(),
false);
376 if (n == (
int)_text_string.size()) {
378 if (_text_string.empty()) {
382 r = _text_block.size() - 1;
383 c = _text_block[r]._string.size();
395 while (r + 1 < (
int)_text_block.size() &&
396 _text_block[r + 1]._row_start < n) {
400 const TextRow &row = _text_block[r];
401 bool is_real_char =
true;
403 nassertr(n > 0,
false);
404 if (row._got_soft_hyphens) {
408 int i = row._row_start;
410 if (_text_string[i]._character != text_soft_hyphen_key &&
411 _text_string[i]._character != text_soft_break_key) {
416 if (_text_string[n - 1]._character != text_soft_hyphen_key &&
417 _text_string[n - 1]._character != text_soft_break_key) {
419 if (_text_string[n - 1]._character ==
'\n') {
420 is_real_char =
false;
423 is_real_char =
false;
428 c = min(n - row._row_start, (
int)row._string.size());
429 if (_text_string[n - 1]._character ==
'\n') {
430 is_real_char =
false;
447 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0);
448 if (r == (
int)_text_block.size()) {
450 return _text_string.size();
453 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0);
454 const TextRow &row = _text_block[r];
456 if (row._got_soft_hyphens) {
459 int n = row._row_start;
461 if (_text_string[n]._character != text_soft_hyphen_key &&
462 _text_string[n]._character != text_soft_break_key) {
471 return row._row_start + c;
486 nassertr(r >= 0 && r <= (
int)_text_block.size(), 0.0f);
487 if (r == (
int)_text_block.size()) {
488 nassertr(c == 0, 0.0f);
492 nassertr(c >= 0 && c <= (
int)_text_block[r]._string.size(), 0.0f);
493 const TextRow &row = _text_block[r];
494 PN_stdfloat xpos = row._xpos;
495 for (
int i = 0; i < c; ++i) {
511 PlacedGlyphs placed_glyphs;
512 assemble_paragraph(placed_glyphs);
520 shadow_node->add_child(shadow_geom_node);
524 text_node->add_child(text_geom_node);
531 bool any_shadow =
false;
533 GeomCollectorMap geom_collector_map;
534 GeomCollectorMap geom_shadow_collector_map;
536 QuadMap quad_shadow_map;
538 PlacedGlyphs::const_iterator pgi;
539 for (pgi = placed_glyphs.begin(); pgi != placed_glyphs.end(); ++pgi) {
540 const GlyphPlacement &placement = (*pgi);
542 if (placement._properties != properties) {
544 properties = placement._properties;
547 if (properties->has_shadow()) {
552 shadow_state.clear();
556 if (!placement._glyph.is_null()) {
557 if (properties->has_shadow()) {
558 if (_dynamic_merge) {
559 if (placement._glyph->has_quad()) {
560 placement.assign_quad_to(quad_shadow_map, shadow_state, shadow);
562 placement.assign_append_to(geom_shadow_collector_map, shadow_state, shadow);
565 placement.assign_to(shadow_geom_node, shadow_state, shadow);
575 if (_dynamic_merge) {
576 if (placement._glyph->has_quad()) {
577 placement.assign_quad_to(quad_map, text_state);
579 placement.assign_append_to(geom_collector_map, text_state);
582 placement.assign_to(text_geom_node, text_state);
585 placement.copy_graphic_to(text_node, text_state);
587 placed_glyphs.clear();
592 parent_node->add_child(shadow_node);
595 GeomCollectorMap::iterator gc;
596 for (gc = geom_collector_map.begin(); gc != geom_collector_map.end(); ++gc) {
597 (*gc).second.append_geom(text_geom_node, (*gc).first._state);
600 generate_quads(text_geom_node, quad_map);
603 for (gc = geom_shadow_collector_map.begin();
604 gc != geom_shadow_collector_map.end();
606 (*gc).second.append_geom(shadow_geom_node, (*gc).first._state);
609 generate_quads(shadow_geom_node, quad_shadow_map);
612 parent_node->add_child(text_node);
626 if (character ==
' ') {
629 nassertr(font !=
nullptr, 0.0f);
636 UnicodeLatinMap::AccentType accent_type;
637 int additional_flags;
638 PN_stdfloat glyph_scale;
639 PN_stdfloat advance_scale;
640 get_character_glyphs(character, &properties,
641 got_glyph, first_glyph, second_glyph, accent_type,
642 additional_flags, glyph_scale, advance_scale);
644 PN_stdfloat advance = 0.0f;
646 if (first_glyph !=
nullptr) {
647 advance = first_glyph->get_advance() * advance_scale;
649 if (second_glyph !=
nullptr) {
650 advance += second_glyph->get_advance();
655 return advance * glyph_scale;
681 if (character ==
' ' || character ==
'\n') {
688 nassertr(font !=
nullptr,
false);
691 return font->get_glyph(character, glyph);
705 if (character ==
' ' || character ==
'\n') {
714 UnicodeLatinMap::AccentType accent_type;
715 int additional_flags;
716 PN_stdfloat glyph_scale;
717 PN_stdfloat advance_scale;
718 get_character_glyphs(character, &properties,
719 got_glyph, first_glyph, second_glyph, accent_type,
720 additional_flags, glyph_scale, advance_scale);
740 if (character ==
' ' || character ==
'\n') {
747 nassertr(font !=
nullptr,
false);
750 if (!font->get_glyph(character, glyph)) {
754 return glyph->is_whitespace();
764 wstring::const_iterator &si,
765 const wstring::const_iterator &send,
766 TextAssembler::ComputedProperties *current_cprops) {
768 if ((*si) == text_push_properties_key) {
774 while (si != send && (*si) != text_push_properties_key) {
782 <<
"Unclosed push_properties in text.\n";
789 PT(ComputedProperties) new_cprops =
790 new ComputedProperties(current_cprops, wname, _encoder);
793 scan_wtext(output_string, si, send, new_cprops);
795 if (text_cat.is_debug()) {
801 <<
"push_properties not matched by pop_properties.\n";
805 }
else if ((*si) == text_pop_properties_key) {
811 }
else if ((*si) == text_embed_graphic_key) {
816 wstring graphic_wname;
818 while (si != send && (*si) != text_embed_graphic_key) {
819 graphic_wname += (*si);
826 <<
"Unclosed embed_graphic in text.\n";
834 std::string graphic_name = _encoder->
encode_wtext(graphic_wname);
841 if (named_graphic !=
nullptr) {
842 output_string.push_back(TextCharacter(named_graphic, graphic_wname, current_cprops));
846 <<
"Unknown TextGraphic: " << graphic_name <<
"\n";
851 output_string.push_back(TextCharacter(*si, current_cprops));
873 if (_text_string.empty()) {
880 _text_block.push_back(TextRow(p));
883 PN_stdfloat initial_width = 0.0f;
884 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
885 if (_text_string[p]._character ==
'\n') {
886 initial_width = 0.0f;
887 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
891 _text_block.back()._eol_cprops = _text_string[p]._cprops;
892 _text_block.push_back(TextRow(p + 1));
895 _text_block.back()._string.push_back(_text_string[p]);
899 bool needs_newline =
false;
901 while (p < _text_string.size()) {
902 nassertr(!isspacew(_text_string[p]._character),
false);
908 bool any_spaces =
false;
909 size_t last_space = 0;
910 PN_stdfloat last_space_width = 0.0f;
912 bool any_hyphens =
false;
913 size_t last_hyphen = 0;
914 bool output_hyphen =
false;
916 bool overflow =
false;
917 PN_stdfloat wordwrap_width = -1.0f;
919 bool last_was_space =
false;
920 PN_stdfloat width = initial_width;
921 while (q < _text_string.size() && _text_string[q]._character !=
'\n') {
922 if (_text_string[q]._cprops->_properties.has_wordwrap()) {
923 wordwrap_width = _text_string[q]._cprops->_properties.get_wordwrap();
925 wordwrap_width = -1.0f;
928 if (isspacew(_text_string[q]._character) ||
929 _text_string[q]._character == text_soft_break_key) {
930 if (!last_was_space) {
937 last_space_width = width;
938 last_was_space =
true;
941 last_was_space =
false;
946 if (_text_string[q]._character == text_soft_hyphen_key) {
947 if (wordwrap_width > 0.0f) {
951 if (q != p && width + calc_hyphen_width(_text_string[q]) <= wordwrap_width) {
963 if (wordwrap_width > 0.0f && width > wordwrap_width) {
975 nassertr(wordwrap_width > 0.0f,
false);
977 if (any_spaces && last_space_width / wordwrap_width >= text_hyphen_ratio) {
986 output_hyphen =
true;
988 }
else if (any_spaces) {
997 while ((
int)i < text_max_never_break && q - i > p &&
998 get_text_never_break_before().find(_text_string[q - i]._character) != wstring::npos) {
1001 if ((
int)i < text_max_never_break) {
1008 size_t next_start = q;
1009 while (next_start < _text_string.size() &&
1010 isbreakpoint(_text_string[next_start]._character)) {
1015 while (q > p && isspacew(_text_string[q - 1]._character)) {
1019 if (next_start == p) {
1024 if (initial_width == 0.0f) {
1030 while (next_start < _text_string.size() &&
1031 isbreakpoint(_text_string[next_start]._character)) {
1037 if (needs_newline) {
1038 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1042 _text_block.push_back(TextRow(p));
1045 needs_newline =
true;
1048 if (_text_string[next_start - 1]._cprops->_properties.get_preserve_trailing_whitespace()) {
1052 for (
size_t pi = p; pi < q; pi++) {
1053 if (_text_string[pi]._character != text_soft_hyphen_key &&
1054 _text_string[pi]._character != text_soft_break_key) {
1055 _text_block.back()._string.push_back(_text_string[pi]);
1057 _text_block.back()._got_soft_hyphens =
true;
1060 if (output_hyphen) {
1061 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1062 wstring::const_iterator wi;
1063 for (wi = text_soft_hyphen_output.begin();
1064 wi != text_soft_hyphen_output.end();
1066 _text_block.back()._string.push_back(TextCharacter(*wi, _text_string[last_hyphen]._cprops));
1072 if (next_start < _text_string.size() && _text_string[next_start]._character ==
'\n') {
1074 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1078 _text_block.back()._eol_cprops = _text_string[next_start]._cprops;
1080 _text_block.push_back(TextRow(next_start));
1081 needs_newline =
false;
1086 initial_width = 0.0f;
1087 while (p < _text_string.size() && isspacew(_text_string[p]._character)) {
1088 if (_text_string[p]._character ==
'\n') {
1089 initial_width = 0.0f;
1090 if (_max_rows > 0 && (
int)_text_block.size() >= _max_rows) {
1094 _text_block.back()._eol_cprops = _text_string[p]._cprops;
1095 _text_block.push_back(TextRow(p + 1));
1097 initial_width +=
calc_width(_text_string[p]);
1098 _text_block.back()._string.push_back(_text_string[p]);
1111PN_stdfloat TextAssembler::
1112calc_hyphen_width(
const TextCharacter &tch) {
1113 TextFont *font = tch._cprops->_properties.get_font();
1114 nassertr(font !=
nullptr, 0.0f);
1116 PN_stdfloat hyphen_width = 0.0f;
1117 wstring text_soft_hyphen_output = get_text_soft_hyphen_output();
1118 wstring::const_iterator wi;
1119 for (wi = text_soft_hyphen_output.begin();
1120 wi != text_soft_hyphen_output.end();
1122 hyphen_width +=
calc_width(*wi, tch._cprops->_properties);
1125 return hyphen_width;
1132generate_quads(
GeomNode *geom_node,
const QuadMap &quad_map) {
1133 QuadMap::const_iterator qmi;
1134 for (qmi = quad_map.begin(); qmi != quad_map.end(); ++qmi) {
1135 const QuadDefs &quads = qmi->second;
1137 glyphs.reserve(quads.size());
1149 vtx_handle->unclean_set_num_rows(quads.size() * 4);
1151 unsigned char *write_ptr = vtx_handle->get_write_pointer();
1155 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float32);
1157 PN_float32 *vtx_ptr = (PN_float32 *)
1158 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1159 PN_float32 *tex_ptr = (PN_float32 *)
1160 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1162 for (
const QuadDef &quad : quads) {
1163 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1165 vtx_ptr[2] = quad._dimensions[3];
1168 tex_ptr[0] = quad._uvs[0];
1169 tex_ptr[1] = quad._uvs[3];
1172 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1174 vtx_ptr[2] = quad._dimensions[1];
1177 tex_ptr[0] = quad._uvs[0];
1178 tex_ptr[1] = quad._uvs[1];
1181 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1183 vtx_ptr[2] = quad._dimensions[3];
1186 tex_ptr[0] = quad._uvs[2];
1187 tex_ptr[1] = quad._uvs[3];
1190 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1192 vtx_ptr[2] = quad._dimensions[1];
1195 tex_ptr[0] = quad._uvs[2];
1196 tex_ptr[1] = quad._uvs[1];
1199 glyphs.push_back(std::move(quad._glyph));
1203 size_t stride = format->
get_array(0)->get_stride() /
sizeof(PN_float64);
1205 PN_float64 *vtx_ptr = (PN_float64 *)
1206 (write_ptr + format->
get_column(InternalName::get_vertex())->get_start());
1207 PN_float64 *tex_ptr = (PN_float64 *)
1208 (write_ptr + format->
get_column(InternalName::get_texcoord())->get_start());
1210 for (
const QuadDef &quad : quads) {
1211 vtx_ptr[0] = quad._dimensions[0] + quad._slanth;
1213 vtx_ptr[2] = quad._dimensions[3];
1216 tex_ptr[0] = quad._uvs[0];
1217 tex_ptr[1] = quad._uvs[3];
1220 vtx_ptr[0] = quad._dimensions[0] + quad._slantl;
1222 vtx_ptr[2] = quad._dimensions[1];
1225 tex_ptr[0] = quad._uvs[0];
1226 tex_ptr[1] = quad._uvs[1];
1229 vtx_ptr[0] = quad._dimensions[2] + quad._slanth;
1231 vtx_ptr[2] = quad._dimensions[3];
1234 tex_ptr[0] = quad._uvs[2];
1235 tex_ptr[1] = quad._uvs[3];
1238 vtx_ptr[0] = quad._dimensions[2] + quad._slantl;
1240 vtx_ptr[2] = quad._dimensions[1];
1243 tex_ptr[0] = quad._uvs[2];
1244 tex_ptr[1] = quad._uvs[1];
1247 glyphs.push_back(std::move(quad._glyph));
1253 int vtx_count = quads.size() * 4;
1255 if (vtx_count > 65535) {
1256 tris->set_index_type(GeomEnums::NT_uint32);
1258 tris->set_index_type(GeomEnums::NT_uint16);
1262 idx_handle->unclean_set_num_rows(quads.size() * 6);
1263 if (tris->get_index_type() == GeomEnums::NT_uint16) {
1265 uint16_t *idx_ptr = (uint16_t *)idx_handle->get_write_pointer();
1267 for (
int i = 0; i < vtx_count; i += 4) {
1268 *(idx_ptr++) = i + 0;
1269 *(idx_ptr++) = i + 1;
1270 *(idx_ptr++) = i + 2;
1271 *(idx_ptr++) = i + 2;
1272 *(idx_ptr++) = i + 1;
1273 *(idx_ptr++) = i + 3;
1277 uint32_t *idx_ptr = (uint32_t *)idx_handle->get_write_pointer();
1279 for (
int i = 0; i < vtx_count; i += 4) {
1280 *(idx_ptr++) = i + 0;
1281 *(idx_ptr++) = i + 1;
1282 *(idx_ptr++) = i + 2;
1283 *(idx_ptr++) = i + 2;
1284 *(idx_ptr++) = i + 1;
1285 *(idx_ptr++) = i + 3;
1291 tris->set_minmax(0, vtx_count - 1,
nullptr,
nullptr);
1294 geom->_glyphs.swap(glyphs);
1295 geom->add_primitive(tris);
1296 geom_node->
add_geom(geom, qmi->first);
1306 _ul.set(0.0f, 0.0f);
1307 _lr.set(0.0f, 0.0f);
1310 PN_stdfloat ypos = 0.0f;
1311 _next_row_ypos = 0.0f;
1312 TextBlock::iterator bi;
1313 for (bi = _text_block.begin(); bi != _text_block.end(); ++bi) {
1314 TextRow &row = (*bi);
1317 size_t first_glyph = placed_glyphs.size();
1320 PN_stdfloat row_width, line_height, wordwrap;
1321 TextProperties::Alignment align;
1322 assemble_row(row, placed_glyphs,
1323 row_width, line_height, align, wordwrap);
1327 if (num_rows == 0) {
1329 _ul[1] = 0.8f * line_height;
1334 ypos -= line_height;
1336 _lr[1] = ypos - 0.2 * line_height;
1343 PN_stdfloat xpos = 0;
1345 case TextProperties::A_left:
1346 _lr[0] = max(_lr[0], row_width);
1349 case TextProperties::A_right:
1351 _ul[0] = min(_ul[0], xpos);
1354 case TextProperties::A_center:
1355 xpos = -0.5f * row_width;
1356 _ul[0] = min(_ul[0], xpos);
1357 _lr[0] = max(_lr[0], -xpos);
1360 case TextProperties::A_boxed_left:
1361 _lr[0] = max(_lr[0], max(row_width, wordwrap));
1364 case TextProperties::A_boxed_right:
1365 xpos = wordwrap - row_width;
1366 _ul[0] = min(_ul[0], xpos);
1369 case TextProperties::A_boxed_center:
1370 xpos = -0.5f * row_width;
1371 if (wordwrap > row_width) xpos += (wordwrap * 0.5f);
1372 _ul[0] = min(_ul[0], max(xpos,(wordwrap * 0.5f)));
1373 _lr[0] = max(_lr[0], min(-xpos,-(wordwrap * 0.5f)));
1381 for (
size_t i = first_glyph; i < placed_glyphs.size(); ++i) {
1382 placed_glyphs[i]._xpos += xpos;
1383 placed_glyphs[i]._ypos += ypos;
1388 _next_row_ypos = ypos - line_height;
1403assemble_row(TextAssembler::TextRow &row,
1405 PN_stdfloat &row_width, PN_stdfloat &line_height,
1406 TextProperties::Alignment &align, PN_stdfloat &wordwrap) {
1410 PN_stdfloat xpos = 0.0f;
1411 align = TextProperties::A_left;
1416 bool underscore =
false;
1417 PN_stdfloat underscore_start = 0.0f;
1421 const ComputedProperties *prev_cprops =
nullptr;
1422 hb_buffer_t *harfbuff =
nullptr;
1425 TextString::const_iterator si;
1426 for (si = row._string.begin(); si != row._string.end(); ++si) {
1427 const TextCharacter &tch = (*si);
1428 wchar_t character = tch._character;
1433 (underscore && (properties->get_text_color() != underscore_properties->get_text_color() ||
1436 if (underscore && underscore_start != xpos) {
1437 draw_underscore(placed_glyphs, underscore_start, xpos,
1438 underscore_properties);
1441 underscore_start = xpos;
1442 underscore_properties = properties;
1446 nassertv(font !=
nullptr);
1449 if ((align == TextProperties::A_left) &&
1450 (properties->get_align() != TextProperties::A_left)) {
1451 align = properties->get_align();
1455 if (properties->get_wordwrap() > 0.0f) {
1456 wordwrap = properties->get_wordwrap();
1461 if (graphic !=
nullptr) {
1463 line_height = max(line_height, frame[3] - frame[2]);
1469 if (tch._cprops != prev_cprops || graphic !=
nullptr) {
1470 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1472 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1473 hb_buffer_reset(harfbuff);
1475 }
else if (harfbuff ==
nullptr && text_use_harfbuzz &&
1476 font->
is_of_type(DynamicTextFont::get_class_type())) {
1477 harfbuff = hb_buffer_create();
1479 prev_cprops = tch._cprops;
1482 if (graphic ==
nullptr && harfbuff !=
nullptr) {
1483 hb_buffer_add(harfbuff, character, character);
1488 if (character ==
' ') {
1493 }
else if (character ==
'\t') {
1496 xpos = (floor(xpos / tab_width) + 1.0f) * tab_width;
1499 }
else if (character == text_soft_hyphen_key) {
1502 }
else if (graphic !=
nullptr) {
1504 GlyphPlacement placement;
1511 model_node->set_preserve_transform(ModelNode::PT_no_touch);
1512 model_node->add_child(model);
1513 placement._graphic_model = model_node.p();
1517 placement._graphic_model = model->copy_subgraph();
1523 PN_stdfloat advance = (frame[1] - frame[0]);
1528 placement._xpos = (xpos - frame[0]);
1530 placement._slant = properties->
get_slant();
1531 placement._properties = properties;
1533 placed_glyphs.push_back(placement);
1535 xpos += advance * glyph_scale;
1543 UnicodeLatinMap::AccentType accent_type;
1544 int additional_flags;
1545 PN_stdfloat glyph_scale;
1546 PN_stdfloat advance_scale;
1547 get_character_glyphs(character, properties,
1548 got_glyph, first_glyph, second_glyph, accent_type,
1549 additional_flags, glyph_scale, advance_scale);
1553 sprintf(buffer,
"U+%04x", character);
1555 <<
"No definition in " << font->get_name()
1556 <<
" for character " << buffer;
1557 if (character < 128 && isprint((
unsigned int)character)) {
1558 text_cat.warning(
false)
1559 <<
" ('" << (char)character <<
"')";
1561 text_cat.warning(
false)
1569 if (prev_char != -1) {
1570 xpos += font->
get_kerning(prev_char, character) * glyph_scale;
1572 prev_char = character;
1579 GlyphPlacement placement;
1581 placement._glyph =
nullptr;
1582 placement._scale = glyph_scale;
1583 placement._xpos = xpos;
1585 placement._slant = properties->
get_slant();
1586 placement._properties = properties;
1588 PN_stdfloat advance = 0.0f;
1590 if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
1593 LPoint3 min_vert, max_vert;
1594 bool found_any =
false;
1595 if (first_glyph !=
nullptr) {
1596 first_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1599 if (second_glyph !=
nullptr) {
1600 second_glyph->calc_tight_bounds(min_vert, max_vert, found_any,
1605 LPoint3 centroid = (min_vert + max_vert) / 2.0f;
1607 if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
1618 placement._scale *= -1;
1619 placement._xpos += centroid[0] * 2;
1620 placement._ypos += centroid[2] * 2;
1623 if (accent_type != UnicodeLatinMap::AT_none) {
1624 GlyphPlacement accent_placement(placement);
1625 tack_on_accent(accent_type, min_vert, max_vert, centroid,
1626 properties, accent_placement);
1627 placed_glyphs.push_back(accent_placement);
1632 if (first_glyph !=
nullptr) {
1633 advance = first_glyph->get_advance() * advance_scale;
1634 if (!first_glyph->is_whitespace()) {
1635 std::swap(placement._glyph, first_glyph);
1636 placed_glyphs.push_back(placement);
1642 if (second_glyph !=
nullptr) {
1643 placement._xpos += advance * glyph_scale;
1644 advance += second_glyph->get_advance();
1645 std::swap(placement._glyph, second_glyph);
1646 placed_glyphs.push_back(placement);
1649 xpos += advance * glyph_scale;
1654 if (harfbuff !=
nullptr && hb_buffer_get_length(harfbuff) > 0) {
1655 shape_buffer(harfbuff, placed_glyphs, xpos, prev_cprops->_properties);
1657 hb_buffer_destroy(harfbuff);
1660 if (underscore && underscore_start != xpos) {
1661 draw_underscore(placed_glyphs, underscore_start, xpos,
1662 underscore_properties);
1667 if (row._eol_cprops !=
nullptr) {
1672 const TextProperties *properties = &(row._eol_cprops->_properties);
1674 nassertv(font !=
nullptr);
1676 if (line_height == 0.0f) {
1678 line_height = max(line_height, font->
get_line_height() * glyph_scale);
1687shape_buffer(hb_buffer_t *buf, PlacedGlyphs &placed_glyphs, PN_stdfloat &xpos,
1693 hb_direction_t direction = HB_DIRECTION_INVALID;
1696 case TextProperties::D_ltr:
1697 direction = HB_DIRECTION_LTR;
1699 case TextProperties::D_rtl:
1700 direction = HB_DIRECTION_RTL;
1704 hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
1705 hb_buffer_set_direction(buf, direction);
1706 hb_buffer_guess_segment_properties(buf);
1708 DynamicTextFont *font = DCAST(DynamicTextFont, properties.
get_font());
1709 hb_font_t *hb_font = font->get_hb_font();
1710 hb_shape(hb_font, buf,
nullptr, 0);
1713 PN_stdfloat scale = glyph_scale / (font->get_pixels_per_unit() * font->get_scale_factor() * 64.0);
1715 unsigned int glyph_count;
1716 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
1717 hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
1719 for (
unsigned int i = 0; i < glyph_count; ++i) {
1720 int character = glyph_info[i].cluster;
1721 int glyph_index = glyph_info[i].codepoint;
1724 if (!font->get_glyph_by_index(character, glyph_index, glyph)) {
1726 sprintf(buffer,
"U+%04x", character);
1728 <<
"No definition in " << font->get_name()
1729 <<
" for character " << buffer;
1730 if (character < 128 && isprint((
unsigned int)character)) {
1731 text_cat.warning(
false)
1732 <<
" ('" << (char)character <<
"')";
1734 text_cat.warning(
false)
1738 PN_stdfloat advance = glyph_pos[i].x_advance * scale;
1739 if (glyph->is_whitespace()) {
1745 PN_stdfloat x_offset = glyph_pos[i].x_offset * scale;
1746 PN_stdfloat y_offset = glyph_pos[i].y_offset * scale;
1752 GlyphPlacement placement;
1753 placement._glyph = std::move(glyph);
1754 placement._scale = glyph_scale;
1755 placement._xpos = xpos + x_offset;
1757 placement._slant = properties.
get_slant();
1758 placement._properties = &properties;
1759 placed_glyphs.push_back(placement);
1772 PN_stdfloat underscore_start, PN_stdfloat underscore_end,
1778 vdata->unclean_set_num_rows(2);
1783 vertex.set_data3(underscore_start, 0.0f, y);
1784 color.set_data4(underscore_properties->get_text_color());
1785 vertex.set_data3(underscore_end, 0.0f, y);
1786 color.set_data4(underscore_properties->get_text_color());
1789 lines->add_next_vertices(2);
1790 lines->close_primitive();
1793 geom->add_primitive(lines);
1802 GlyphPlacement placement;
1803 placement._glyph = std::move(glyph);
1804 placement._xpos = 0;
1805 placement._ypos = 0;
1806 placement._scale = 1;
1807 placement._slant = 0;
1808 placement._properties = underscore_properties;
1809 placed_glyphs.push_back(placement);
1825get_character_glyphs(
int character,
const TextProperties *properties,
1828 UnicodeLatinMap::AccentType &accent_type,
1829 int &additional_flags,
1830 PN_stdfloat &glyph_scale, PN_stdfloat &advance_scale) {
1832 nassertv_always(font !=
nullptr);
1836 second_glyph =
nullptr;
1837 accent_type = UnicodeLatinMap::AT_none;
1838 additional_flags = 0;
1840 advance_scale = 1.0f;
1846 if (map_entry !=
nullptr) {
1848 map_entry->_toupper_character != (
char32_t)character) {
1849 character = map_entry->_toupper_character;
1855 got_glyph = font->get_glyph(character, glyph);
1856 if (!got_glyph && map_entry !=
nullptr && map_entry->_ascii_equiv != 0) {
1859 if (map_entry->_ascii_equiv ==
'i') {
1861 got_glyph = font->get_glyph(0x0131, glyph) ||
1862 font->get_glyph(
'i', glyph);
1864 }
else if (map_entry->_ascii_equiv ==
'j') {
1866 got_glyph = font->get_glyph(0x0237, glyph) ||
1867 font->get_glyph(
'j', glyph);
1870 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1873 if (!got_glyph && map_entry->_toupper_character != (
char32_t)character) {
1875 character = map_entry->_toupper_character;
1877 if (map_entry !=
nullptr) {
1878 got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
1883 accent_type = map_entry->_accent_type;
1884 additional_flags = map_entry->_additional_flags;
1886 bool got_second_glyph =
false;
1887 if (map_entry->_ascii_additional != 0) {
1890 font->get_glyph(map_entry->_ascii_additional, second_glyph);
1893 if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
1898 advance_scale = ligature_advance_scale;
1901 if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
1914tack_on_accent(UnicodeLatinMap::AccentType accent_type,
1915 const LPoint3 &min_vert,
const LPoint3 &max_vert,
1916 const LPoint3 ¢roid,
1918 TextAssembler::GlyphPlacement &placement)
const {
1922 if (combine_char != 0 &&
1923 tack_on_accent(combine_char, CP_above, CT_none, min_vert, max_vert,
1924 centroid, properties, placement)) {
1929 switch (accent_type) {
1930 case UnicodeLatinMap::AT_grave:
1935 tack_on_accent(
'/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
1936 properties, placement);
1939 case UnicodeLatinMap::AT_acute:
1940 tack_on_accent(
'/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
1941 properties, placement);
1944 case UnicodeLatinMap::AT_breve:
1945 tack_on_accent(
')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1946 properties, placement);
1949 case UnicodeLatinMap::AT_inverted_breve:
1950 tack_on_accent(
'(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
1951 properties, placement);
1954 case UnicodeLatinMap::AT_circumflex:
1955 tack_on_accent(
'^', CP_above, CT_none, min_vert, max_vert, centroid,
1956 properties, placement) ||
1957 tack_on_accent(
'v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
1958 properties, placement);
1961 case UnicodeLatinMap::AT_circumflex_below:
1962 tack_on_accent(
'^', CP_below, CT_none, min_vert, max_vert, centroid,
1963 properties, placement) ||
1964 tack_on_accent(
'v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
1965 properties, placement);
1968 case UnicodeLatinMap::AT_caron:
1969 tack_on_accent(
'^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
1970 properties, placement) ||
1971 tack_on_accent(
'v', CP_above, CT_squash, min_vert, max_vert, centroid,
1972 properties, placement);
1976 case UnicodeLatinMap::AT_tilde:
1977 tack_on_accent(
'~', CP_above, CT_none, min_vert, max_vert, centroid,
1978 properties, placement) ||
1979 tack_on_accent(
's', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1980 properties, placement);
1984 case UnicodeLatinMap::AT_tilde_below:
1985 tack_on_accent(
'~', CP_below, CT_none, min_vert, max_vert, centroid,
1986 properties, placement) ||
1987 tack_on_accent(
's', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
1988 properties, placement);
1991 case UnicodeLatinMap::AT_diaeresis:
1992 tack_on_accent(
':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
1993 properties, placement);
1996 case UnicodeLatinMap::AT_diaeresis_below:
1997 tack_on_accent(
':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
1998 properties, placement);
2001 case UnicodeLatinMap::AT_dot_above:
2002 tack_on_accent(
'.', CP_above, CT_none, min_vert, max_vert, centroid,
2003 properties, placement);
2006 case UnicodeLatinMap::AT_dot_below:
2007 tack_on_accent(
'.', CP_below, CT_none, min_vert, max_vert, centroid,
2008 properties, placement);
2011 case UnicodeLatinMap::AT_macron:
2012 tack_on_accent(
'-', CP_above, CT_none, min_vert, max_vert, centroid,
2013 properties, placement);
2016 case UnicodeLatinMap::AT_line_below:
2017 tack_on_accent(
'-', CP_below, CT_none, min_vert, max_vert, centroid,
2018 properties, placement);
2021 case UnicodeLatinMap::AT_ring_above:
2022 tack_on_accent(
'o', CP_top, CT_tiny, min_vert, max_vert, centroid,
2023 properties, placement);
2026 case UnicodeLatinMap::AT_ring_below:
2027 tack_on_accent(
'o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
2028 properties, placement);
2031 case UnicodeLatinMap::AT_cedilla:
2032 tack_on_accent(0xb8, CP_below, CT_none, min_vert, max_vert, centroid,
2033 properties, placement) ||
2034 tack_on_accent(
'c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
2035 properties, placement);
2040 case UnicodeLatinMap::AT_comma_below:
2041 tack_on_accent(
',', CP_below, CT_none, min_vert, max_vert, centroid,
2042 properties, placement);
2045 case UnicodeLatinMap::AT_ogonek:
2046 tack_on_accent(
',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
2047 properties, placement);
2050 case UnicodeLatinMap::AT_stroke:
2051 tack_on_accent(
'/', CP_within, CT_none, min_vert, max_vert, centroid,
2052 properties, placement);
2067tack_on_accent(
wchar_t accent_mark, TextAssembler::CheesyPosition position,
2068 TextAssembler::CheesyTransform transform,
2069 const LPoint3 &min_vert,
const LPoint3 &max_vert,
2070 const LPoint3 ¢roid,
2072 TextAssembler::GlyphPlacement &placement)
const {
2074 nassertr(font !=
nullptr,
false);
2079 if (font->get_glyph(accent_mark, accent_glyph) ||
2080 font->get_glyph(toupper(accent_mark), accent_glyph)) {
2081 if (!accent_glyph->is_whitespace()) {
2082 LPoint3 min_accent, max_accent;
2083 bool found_any =
false;
2084 accent_glyph->calc_tight_bounds(min_accent, max_accent, found_any,
2088 LMatrix4 accent_mat;
2089 bool has_mat =
true;
2091 switch (transform) {
2097 accent_mat = LMatrix4::scale_mat(-1.0f, -1.0f, 1.0f);
2099 min_accent[0] = -max_accent[0];
2104 accent_mat = LMatrix4::scale_mat(1.0f, -1.0f, -1.0f);
2106 min_accent[2] = -max_accent[2];
2111 accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2115 max_accent[0] = -min_accent[2];
2116 min_accent[0] = -max_accent[2];
2123 placement._scale *= -1;
2125 min_accent[0] = -max_accent[0];
2128 min_accent[2] = -max_accent[2];
2133 accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f));
2137 min_accent[0] = min_accent[2];
2138 max_accent[0] = max_accent[2];
2144 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
2145 min_accent[0] *= squash_accent_scale_x;
2146 max_accent[0] *= squash_accent_scale_x;
2147 min_accent[2] *= squash_accent_scale_y;
2148 max_accent[2] *= squash_accent_scale_y;
2151 case CT_squash_mirror_y:
2152 accent_mat = LMatrix4::scale_mat(squash_accent_scale_x, -1.0f, -squash_accent_scale_y);
2153 min_accent[0] *= squash_accent_scale_x;
2154 max_accent[0] *= squash_accent_scale_x;
2156 min_accent[2] = -max_accent[2] * squash_accent_scale_y;
2157 max_accent[2] = -t * squash_accent_scale_y;
2160 case CT_squash_mirror_diag:
2162 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2163 LMatrix4::scale_mat(-squash_accent_scale_x, -1.0f, squash_accent_scale_y);
2168 min_accent[0] = min_accent[2] * -squash_accent_scale_x;
2169 max_accent[0] = max_accent[2] * -squash_accent_scale_x;
2170 min_accent[2] = -u * squash_accent_scale_y;
2171 max_accent[2] = -t * squash_accent_scale_y;
2174 case CT_small_squash:
2175 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
2176 min_accent[0] *= small_squash_accent_scale_x;
2177 max_accent[0] *= small_squash_accent_scale_x;
2178 min_accent[2] *= small_squash_accent_scale_y;
2179 max_accent[2] *= small_squash_accent_scale_y;
2182 case CT_small_squash_mirror_y:
2183 accent_mat = LMatrix4::scale_mat(small_squash_accent_scale_x, -1.0f, -small_squash_accent_scale_y);
2184 min_accent[0] *= small_squash_accent_scale_x;
2185 max_accent[0] *= small_squash_accent_scale_x;
2187 min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
2188 max_accent[2] = -t * small_squash_accent_scale_y;
2191 case CT_small_squash_mirror_diag:
2193 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2194 LMatrix4::scale_mat(-small_squash_accent_scale_x, -1.0f, small_squash_accent_scale_y);
2199 min_accent[0] = min_accent[2] * -small_squash_accent_scale_x;
2200 max_accent[0] = max_accent[2] * -small_squash_accent_scale_x;
2201 min_accent[2] = -u * small_squash_accent_scale_y;
2202 max_accent[2] = -t * small_squash_accent_scale_y;
2207 placement._scale *= small_accent_scale;
2208 min_accent *= small_accent_scale;
2209 max_accent *= small_accent_scale;
2212 case CT_small_rotate_270:
2214 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2215 LMatrix4::scale_mat(small_accent_scale);
2220 min_accent[0] = min_accent[2] * small_accent_scale;
2221 max_accent[0] = max_accent[2] * small_accent_scale;
2222 min_accent[2] = -u * small_accent_scale;
2223 max_accent[2] = -t * small_accent_scale;
2228 placement._scale *= tiny_accent_scale;
2229 min_accent *= tiny_accent_scale;
2230 max_accent *= tiny_accent_scale;
2233 case CT_tiny_mirror_x:
2234 accent_mat = LMatrix4::scale_mat(-tiny_accent_scale, -1.0f, tiny_accent_scale);
2237 min_accent[0] = -max_accent[0] * tiny_accent_scale;
2238 max_accent[0] = -t * tiny_accent_scale;
2239 min_accent[2] *= tiny_accent_scale;
2240 max_accent[2] *= tiny_accent_scale;
2243 case CT_tiny_rotate_270:
2245 LMatrix4::rotate_mat_normaxis(270.0f, LVecBase3(0.0f, -1.0f, 0.0f)) *
2246 LMatrix4::scale_mat(tiny_accent_scale);
2251 min_accent[0] = min_accent[2] * tiny_accent_scale;
2252 max_accent[0] = max_accent[2] * tiny_accent_scale;
2253 min_accent[2] = -u * tiny_accent_scale;
2254 max_accent[2] = -t * tiny_accent_scale;
2263 LPoint3 accent_centroid = (min_accent + max_accent) / 2.0f;
2264 PN_stdfloat accent_height = max_accent[2] - min_accent[2] - total_margin * 2;
2265 PN_stdfloat accent_x = centroid[0] - accent_centroid[0];
2266 PN_stdfloat accent_y = 0;
2267 PN_stdfloat min_y = min_vert[2] + total_margin;
2268 PN_stdfloat max_y = max_vert[2] - total_margin;
2273 accent_y = max_y - accent_centroid[2] + accent_height * 0.75f;
2278 accent_y = min_y - accent_centroid[2] - accent_height * 0.75f;
2283 accent_y = max_y - accent_centroid[2];
2288 accent_y = min_y - accent_centroid[2];
2293 accent_y = centroid[2] - accent_centroid[2];
2297 placement._xpos += placement._scale * (accent_x + placement._slant * accent_y);
2298 placement._ypos += placement._scale * accent_y;
2302 PT(
Geom) accent_geom = accent_glyph->get_geom(_usage_hint);
2303 accent_geom->transform_vertices(accent_mat);
2304 placement._glyph =
new TextGlyph(0, accent_geom, accent_glyph->get_state(), 0);
2307 placement._glyph = accent_glyph;
2321void TextAssembler::ComputedProperties::
2322append_delta(wstring &wtext, TextAssembler::ComputedProperties *other) {
2323 if (
this != other) {
2324 if (_depth > other->_depth) {
2326 nassertv(_based_on !=
nullptr);
2328 wtext.push_back(text_pop_properties_key);
2329 _based_on->append_delta(wtext, other);
2331 }
else if (other->_depth > _depth) {
2333 nassertv(other->_based_on !=
nullptr);
2335 append_delta(wtext, other->_based_on);
2336 wtext.push_back(text_push_properties_key);
2337 wtext += other->_wname;
2338 wtext.push_back(text_push_properties_key);
2340 }
else if (_depth != 0) {
2342 nassertv(_based_on !=
nullptr && other->_based_on !=
nullptr);
2344 wtext.push_back(text_pop_properties_key);
2345 _based_on->append_delta(wtext, other->_based_on);
2346 wtext.push_back(text_push_properties_key);
2347 wtext += other->_wname;
2348 wtext.push_back(text_push_properties_key);
2357void TextAssembler::GlyphPlacement::
2359 const LVector2 &offset)
const {
2361 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2362 0.0f, 1.0f, 0.0f, 0.0f,
2363 _slant * _scale, 0.0f, _scale, 0.0f,
2364 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2366 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2367 geom->transform_vertices(xform);
2368 geom_node->
add_geom(geom, state->compose(_glyph->get_state()));
2376void TextAssembler::GlyphPlacement::
2377assign_append_to(GeomCollectorMap &geom_collector_map,
2379 const LVector2 &offset)
const {
2381 LMatrix4 xform(_scale, 0.0f, 0.0f, 0.0f,
2382 0.0f, 1.0f, 0.0f, 0.0f,
2383 _slant * _scale, 0.0f, _scale, 0.0f,
2384 _xpos + offset[0], 0.0f, _ypos - offset[1], 1.0f);
2386 PT(
Geom) geom = _glyph->get_geom(GeomEnums::UH_static);
2391 CPT(
RenderState) rs = _glyph->get_state()->compose(state);
2392 GeomCollectorKey key(rs, vdata->
get_format());
2394 GeomCollectorMap::iterator mi = geom_collector_map.find(key);
2395 if (mi == geom_collector_map.end()) {
2396 mi = geom_collector_map.insert(GeomCollectorMap::value_type(key, GeomCollector(vdata->
get_format()))).first;
2398 GeomCollector &geom_collector = (*mi).second;
2399 geom_collector.count_geom(geom);
2403 VertexIndexMap vimap;
2405 for (
size_t p = 0; p < geom->get_num_primitives(); ++p) {
2406 CPT(
GeomPrimitive) primitive = geom->get_primitive(p)->decompose();
2409 GeomPrimitive *new_prim = geom_collector.get_primitive(primitive->get_type());
2412 for (sp = 0; sp < primitive->get_num_primitives(); sp++) {
2414 e = primitive->get_primitive_end(sp);
2417 for (i = s; i < e; i++) {
2418 int vi = primitive->get_vertex(i);
2421 std::pair<VertexIndexMap::iterator, bool> added = vimap.insert(VertexIndexMap::value_type(vi, 0));
2426 new_vertex = geom_collector.append_vertex(vdata, vi, xform);
2428 (*(added.first)).second = new_vertex;
2434 new_vertex = (*(added.first)).second;
2447void TextAssembler::GlyphPlacement::
2448assign_quad_to(QuadMap &quad_map,
const RenderState *state,
2449 const LVector2 &offset)
const {
2452 if (_glyph->get_quad(quad._dimensions, quad._uvs)) {
2453 quad._dimensions *= _scale;
2454 quad._slantl = quad._dimensions[1] * _slant;
2455 quad._slanth = quad._dimensions[3] * _slant;
2456 quad._dimensions += LVecBase4(_xpos, _ypos, _xpos, _ypos);
2457 quad._dimensions += LVecBase4(offset[0], -offset[1], offset[0], -offset[1]);
2458 quad._glyph = _glyph;
2460 quad_map[state->compose(_glyph->get_state())].push_back(std::move(quad));
2468void TextAssembler::GlyphPlacement::
2470 if (_graphic_model !=
nullptr) {
2473 node->add_child(intermediate_node);
2475 intermediate_node->set_transform(
2476 TransformState::make_pos_hpr_scale_shear(
2477 LVecBase3(_xpos, 0, _ypos),
2479 LVecBase3(_scale, 1, _scale),
2480 LVecBase3(0, _slant, 0)
2483 intermediate_node->set_state(state);
2484 intermediate_node->add_child(_graphic_model);
2492TextAssembler::GeomCollector::
2502TextAssembler::GeomCollector::
2503GeomCollector(
const TextAssembler::GeomCollector ©) :
2504 _vdata(copy._vdata),
2516 if (prim_type == GeomTriangles::get_class_type()) {
2517 if (_triangles ==
nullptr) {
2519 _geom->add_primitive(_triangles);
2523 }
else if (prim_type == GeomLines::get_class_type()) {
2524 if (_lines ==
nullptr) {
2525 _lines =
new GeomLines(Geom::UH_static);
2526 _geom->add_primitive(_lines);
2530 }
else if (prim_type == GeomPoints::get_class_type()) {
2531 if (_points ==
nullptr) {
2533 _geom->add_primitive(_points);
2538 nassert_raise(
"unexpected primitive type");
2546int TextAssembler::GeomCollector::
2548 const LMatrix4 &xform) {
2549 int new_row = _vdata->get_num_rows();
2553 vertex_rewriter.set_row_unsafe(new_row);
2554 LPoint3 point = vertex_rewriter.get_data3();
2555 vertex_rewriter.set_data3(point * xform);
2564void TextAssembler::GeomCollector::
2566 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.