2 * Copyright © 1998-2004 David Turner and Werner Lemberg
3 * Copyright © 2006 Behdad Esfahbod
4 * Copyright © 2007,2008,2009 Red Hat, Inc.
5 * Copyright © 2012 Google, Inc.
7 * This is part of HarfBuzz, a text shaping library.
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and its documentation for any purpose, provided that the
12 * above copyright notice and the following two paragraphs appear in
13 * all copies of this software.
15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
27 * Red Hat Author(s): Behdad Esfahbod
28 * Google Author(s): Behdad Esfahbod
31 #include "hb-ot-layout-private.hh"
33 #include "hb-ot-layout-gdef-table.hh"
34 #include "hb-ot-layout-gsub-table.hh"
35 #include "hb-ot-layout-gpos-table.hh"
41 HB_SHAPER_DATA_ENSURE_DECLARE(ot
, face
)
44 _hb_ot_layout_create (hb_face_t
*face
)
46 hb_ot_layout_t
*layout
= (hb_ot_layout_t
*) calloc (1, sizeof (hb_ot_layout_t
));
47 if (unlikely (!layout
))
50 layout
->gdef_blob
= OT::Sanitizer
<OT::GDEF
>::sanitize (face
->reference_table (HB_OT_TAG_GDEF
));
51 layout
->gdef
= OT::Sanitizer
<OT::GDEF
>::lock_instance (layout
->gdef_blob
);
53 layout
->gsub_blob
= OT::Sanitizer
<OT::GSUB
>::sanitize (face
->reference_table (HB_OT_TAG_GSUB
));
54 layout
->gsub
= OT::Sanitizer
<OT::GSUB
>::lock_instance (layout
->gsub_blob
);
56 layout
->gpos_blob
= OT::Sanitizer
<OT::GPOS
>::sanitize (face
->reference_table (HB_OT_TAG_GPOS
));
57 layout
->gpos
= OT::Sanitizer
<OT::GPOS
>::lock_instance (layout
->gpos_blob
);
59 layout
->gsub_lookup_count
= layout
->gsub
->get_lookup_count ();
60 layout
->gpos_lookup_count
= layout
->gpos
->get_lookup_count ();
62 layout
->gsub_digests
= (hb_set_digest_t
*) calloc (layout
->gsub
->get_lookup_count (), sizeof (hb_set_digest_t
));
63 layout
->gpos_digests
= (hb_set_digest_t
*) calloc (layout
->gpos
->get_lookup_count (), sizeof (hb_set_digest_t
));
65 if (unlikely ((layout
->gsub_lookup_count
&& !layout
->gsub_digests
) ||
66 (layout
->gpos_lookup_count
&& !layout
->gpos_digests
)))
68 _hb_ot_layout_destroy (layout
);
72 for (unsigned int i
= 0; i
< layout
->gsub_lookup_count
; i
++)
74 layout
->gsub_digests
[i
].init ();
75 layout
->gsub
->get_lookup (i
).add_coverage (&layout
->gsub_digests
[i
]);
77 for (unsigned int i
= 0; i
< layout
->gpos_lookup_count
; i
++)
79 layout
->gpos_digests
[i
].init ();
80 layout
->gpos
->get_lookup (i
).add_coverage (&layout
->gpos_digests
[i
]);
87 _hb_ot_layout_destroy (hb_ot_layout_t
*layout
)
89 hb_blob_destroy (layout
->gdef_blob
);
90 hb_blob_destroy (layout
->gsub_blob
);
91 hb_blob_destroy (layout
->gpos_blob
);
93 free (layout
->gsub_digests
);
94 free (layout
->gpos_digests
);
99 static inline const OT::GDEF
&
100 _get_gdef (hb_face_t
*face
)
102 if (unlikely (!hb_ot_shaper_face_data_ensure (face
))) return OT::Null(OT::GDEF
);
103 return *hb_ot_layout_from_face (face
)->gdef
;
105 static inline const OT::GSUB
&
106 _get_gsub (hb_face_t
*face
)
108 if (unlikely (!hb_ot_shaper_face_data_ensure (face
))) return OT::Null(OT::GSUB
);
109 return *hb_ot_layout_from_face (face
)->gsub
;
111 static inline const OT::GPOS
&
112 _get_gpos (hb_face_t
*face
)
114 if (unlikely (!hb_ot_shaper_face_data_ensure (face
))) return OT::Null(OT::GPOS
);
115 return *hb_ot_layout_from_face (face
)->gpos
;
124 hb_ot_layout_has_glyph_classes (hb_face_t
*face
)
126 return _get_gdef (face
).has_glyph_classes ();
129 hb_ot_layout_glyph_class_t
130 hb_ot_layout_get_glyph_class (hb_face_t
*face
,
131 hb_codepoint_t glyph
)
133 return (hb_ot_layout_glyph_class_t
) _get_gdef (face
).get_glyph_class (glyph
);
137 hb_ot_layout_get_glyphs_in_class (hb_face_t
*face
,
138 hb_ot_layout_glyph_class_t klass
,
139 hb_set_t
*glyphs
/* OUT */)
141 return _get_gdef (face
).get_glyphs_in_class (klass
, glyphs
);
145 hb_ot_layout_get_attach_points (hb_face_t
*face
,
146 hb_codepoint_t glyph
,
147 unsigned int start_offset
,
148 unsigned int *point_count
/* IN/OUT */,
149 unsigned int *point_array
/* OUT */)
151 return _get_gdef (face
).get_attach_points (glyph
, start_offset
, point_count
, point_array
);
155 hb_ot_layout_get_ligature_carets (hb_font_t
*font
,
156 hb_direction_t direction
,
157 hb_codepoint_t glyph
,
158 unsigned int start_offset
,
159 unsigned int *caret_count
/* IN/OUT */,
160 int *caret_array
/* OUT */)
162 return _get_gdef (font
->face
).get_lig_carets (font
, direction
, glyph
, start_offset
, caret_count
, caret_array
);
170 static const OT::GSUBGPOS
&
171 get_gsubgpos_table (hb_face_t
*face
,
175 case HB_OT_TAG_GSUB
: return _get_gsub (face
);
176 case HB_OT_TAG_GPOS
: return _get_gpos (face
);
177 default: return OT::Null(OT::GSUBGPOS
);
183 hb_ot_layout_table_get_script_tags (hb_face_t
*face
,
185 unsigned int start_offset
,
186 unsigned int *script_count
/* IN/OUT */,
187 hb_tag_t
*script_tags
/* OUT */)
189 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
191 return g
.get_script_tags (start_offset
, script_count
, script_tags
);
194 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n')
197 hb_ot_layout_table_find_script (hb_face_t
*face
,
200 unsigned int *script_index
)
202 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX
== HB_OT_LAYOUT_NO_SCRIPT_INDEX
);
203 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
205 if (g
.find_script_index (script_tag
, script_index
))
208 /* try finding 'DFLT' */
209 if (g
.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT
, script_index
))
212 /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
213 * including many versions of DejaVu Sans Mono! */
214 if (g
.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE
, script_index
))
217 /* try with 'latn'; some old fonts put their features there even though
218 they're really trying to support Thai, for example :( */
219 if (g
.find_script_index (HB_OT_TAG_LATIN_SCRIPT
, script_index
))
222 if (script_index
) *script_index
= HB_OT_LAYOUT_NO_SCRIPT_INDEX
;
227 hb_ot_layout_table_choose_script (hb_face_t
*face
,
229 const hb_tag_t
*script_tags
,
230 unsigned int *script_index
,
231 hb_tag_t
*chosen_script
)
233 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX
== HB_OT_LAYOUT_NO_SCRIPT_INDEX
);
234 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
238 if (g
.find_script_index (*script_tags
, script_index
)) {
240 *chosen_script
= *script_tags
;
246 /* try finding 'DFLT' */
247 if (g
.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT
, script_index
)) {
249 *chosen_script
= HB_OT_TAG_DEFAULT_SCRIPT
;
253 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
254 if (g
.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE
, script_index
)) {
256 *chosen_script
= HB_OT_TAG_DEFAULT_LANGUAGE
;
260 /* try with 'latn'; some old fonts put their features there even though
261 they're really trying to support Thai, for example :( */
262 if (g
.find_script_index (HB_OT_TAG_LATIN_SCRIPT
, script_index
)) {
264 *chosen_script
= HB_OT_TAG_LATIN_SCRIPT
;
268 if (script_index
) *script_index
= HB_OT_LAYOUT_NO_SCRIPT_INDEX
;
270 *chosen_script
= HB_OT_LAYOUT_NO_SCRIPT_INDEX
;
275 hb_ot_layout_table_get_feature_tags (hb_face_t
*face
,
277 unsigned int start_offset
,
278 unsigned int *feature_count
/* IN/OUT */,
279 hb_tag_t
*feature_tags
/* OUT */)
281 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
283 return g
.get_feature_tags (start_offset
, feature_count
, feature_tags
);
288 hb_ot_layout_script_get_language_tags (hb_face_t
*face
,
290 unsigned int script_index
,
291 unsigned int start_offset
,
292 unsigned int *language_count
/* IN/OUT */,
293 hb_tag_t
*language_tags
/* OUT */)
295 const OT::Script
&s
= get_gsubgpos_table (face
, table_tag
).get_script (script_index
);
297 return s
.get_lang_sys_tags (start_offset
, language_count
, language_tags
);
301 hb_ot_layout_script_find_language (hb_face_t
*face
,
303 unsigned int script_index
,
304 hb_tag_t language_tag
,
305 unsigned int *language_index
)
307 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX
== HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
);
308 const OT::Script
&s
= get_gsubgpos_table (face
, table_tag
).get_script (script_index
);
310 if (s
.find_lang_sys_index (language_tag
, language_index
))
313 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
314 if (s
.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE
, language_index
))
317 if (language_index
) *language_index
= HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
;
322 hb_ot_layout_language_get_required_feature_index (hb_face_t
*face
,
324 unsigned int script_index
,
325 unsigned int language_index
,
326 unsigned int *feature_index
)
328 const OT::LangSys
&l
= get_gsubgpos_table (face
, table_tag
).get_script (script_index
).get_lang_sys (language_index
);
330 if (feature_index
) *feature_index
= l
.get_required_feature_index ();
332 return l
.has_required_feature ();
336 hb_ot_layout_language_get_feature_indexes (hb_face_t
*face
,
338 unsigned int script_index
,
339 unsigned int language_index
,
340 unsigned int start_offset
,
341 unsigned int *feature_count
/* IN/OUT */,
342 unsigned int *feature_indexes
/* OUT */)
344 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
345 const OT::LangSys
&l
= g
.get_script (script_index
).get_lang_sys (language_index
);
347 return l
.get_feature_indexes (start_offset
, feature_count
, feature_indexes
);
351 hb_ot_layout_language_get_feature_tags (hb_face_t
*face
,
353 unsigned int script_index
,
354 unsigned int language_index
,
355 unsigned int start_offset
,
356 unsigned int *feature_count
/* IN/OUT */,
357 hb_tag_t
*feature_tags
/* OUT */)
359 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
360 const OT::LangSys
&l
= g
.get_script (script_index
).get_lang_sys (language_index
);
362 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t
));
363 unsigned int ret
= l
.get_feature_indexes (start_offset
, feature_count
, (unsigned int *) feature_tags
);
366 unsigned int count
= *feature_count
;
367 for (unsigned int i
= 0; i
< count
; i
++)
368 feature_tags
[i
] = g
.get_feature_tag ((unsigned int) feature_tags
[i
]);
376 hb_ot_layout_language_find_feature (hb_face_t
*face
,
378 unsigned int script_index
,
379 unsigned int language_index
,
380 hb_tag_t feature_tag
,
381 unsigned int *feature_index
)
383 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX
== HB_OT_LAYOUT_NO_FEATURE_INDEX
);
384 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
385 const OT::LangSys
&l
= g
.get_script (script_index
).get_lang_sys (language_index
);
387 unsigned int num_features
= l
.get_feature_count ();
388 for (unsigned int i
= 0; i
< num_features
; i
++) {
389 unsigned int f_index
= l
.get_feature_index (i
);
391 if (feature_tag
== g
.get_feature_tag (f_index
)) {
392 if (feature_index
) *feature_index
= f_index
;
397 if (feature_index
) *feature_index
= HB_OT_LAYOUT_NO_FEATURE_INDEX
;
402 hb_ot_layout_feature_get_lookups (hb_face_t
*face
,
404 unsigned int feature_index
,
405 unsigned int start_offset
,
406 unsigned int *lookup_count
/* IN/OUT */,
407 unsigned int *lookup_indexes
/* OUT */)
409 const OT::GSUBGPOS
&g
= get_gsubgpos_table (face
, table_tag
);
410 const OT::Feature
&f
= g
.get_feature (feature_index
);
412 return f
.get_lookup_indexes (start_offset
, lookup_count
, lookup_indexes
);
416 _hb_ot_layout_collect_lookups_lookups (hb_face_t
*face
,
418 unsigned int feature_index
,
419 hb_set_t
*lookup_indexes
/* OUT */)
421 unsigned int lookup_indices
[32];
422 unsigned int offset
, len
;
426 len
= ARRAY_LENGTH (lookup_indices
);
427 hb_ot_layout_feature_get_lookups (face
,
433 for (unsigned int i
= 0; i
< len
; i
++)
434 lookup_indexes
->add (lookup_indices
[i
]);
437 } while (len
== ARRAY_LENGTH (lookup_indices
));
441 _hb_ot_layout_collect_lookups_features (hb_face_t
*face
,
443 unsigned int script_index
,
444 unsigned int language_index
,
445 const hb_tag_t
*features
,
446 hb_set_t
*lookup_indexes
/* OUT */)
448 unsigned int required_feature_index
;
449 if (hb_ot_layout_language_get_required_feature_index (face
,
453 &required_feature_index
))
454 _hb_ot_layout_collect_lookups_lookups (face
,
456 required_feature_index
,
462 unsigned int feature_indices
[32];
463 unsigned int offset
, len
;
467 len
= ARRAY_LENGTH (feature_indices
);
468 hb_ot_layout_language_get_feature_indexes (face
,
475 for (unsigned int i
= 0; i
< len
; i
++)
476 _hb_ot_layout_collect_lookups_lookups (face
,
482 } while (len
== ARRAY_LENGTH (feature_indices
));
486 for (; *features
; features
++)
488 unsigned int feature_index
;
489 if (hb_ot_layout_language_find_feature (face
,
495 _hb_ot_layout_collect_lookups_lookups (face
,
504 _hb_ot_layout_collect_lookups_languages (hb_face_t
*face
,
506 unsigned int script_index
,
507 const hb_tag_t
*languages
,
508 const hb_tag_t
*features
,
509 hb_set_t
*lookup_indexes
/* OUT */)
511 _hb_ot_layout_collect_lookups_features (face
,
514 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
,
521 unsigned int count
= hb_ot_layout_script_get_language_tags (face
,
525 for (unsigned int language_index
= 0; language_index
< count
; language_index
++)
526 _hb_ot_layout_collect_lookups_features (face
,
535 for (; *languages
; languages
++)
537 unsigned int language_index
;
538 if (hb_ot_layout_script_find_language (face
,
543 _hb_ot_layout_collect_lookups_features (face
,
554 hb_ot_layout_collect_lookups (hb_face_t
*face
,
556 const hb_tag_t
*scripts
,
557 const hb_tag_t
*languages
,
558 const hb_tag_t
*features
,
559 hb_set_t
*lookup_indexes
/* OUT */)
564 unsigned int count
= hb_ot_layout_table_get_script_tags (face
,
567 for (unsigned int script_index
= 0; script_index
< count
; script_index
++)
568 _hb_ot_layout_collect_lookups_languages (face
,
577 for (; *scripts
; scripts
++)
579 unsigned int script_index
;
580 if (hb_ot_layout_table_find_script (face
,
584 _hb_ot_layout_collect_lookups_languages (face
,
595 hb_ot_layout_lookup_collect_glyphs (hb_face_t
*face
,
597 unsigned int lookup_index
,
598 hb_set_t
*glyphs_before
, /* OUT. May be NULL */
599 hb_set_t
*glyphs_input
, /* OUT. May be NULL */
600 hb_set_t
*glyphs_after
, /* OUT. May be NULL */
601 hb_set_t
*glyphs_output
/* OUT. May be NULL */)
603 if (unlikely (!hb_ot_shaper_face_data_ensure (face
))) return;
605 OT::hb_collect_glyphs_context_t
c (face
,
615 const OT::SubstLookup
& l
= hb_ot_layout_from_face (face
)->gsub
->get_lookup (lookup_index
);
616 l
.collect_glyphs_lookup (&c
);
621 const OT::PosLookup
& l
= hb_ot_layout_from_face (face
)->gpos
->get_lookup (lookup_index
);
622 l
.collect_glyphs_lookup (&c
);
634 hb_ot_layout_has_substitution (hb_face_t
*face
)
636 return &_get_gsub (face
) != &OT::Null(OT::GSUB
);
640 hb_ot_layout_lookup_would_substitute (hb_face_t
*face
,
641 unsigned int lookup_index
,
642 const hb_codepoint_t
*glyphs
,
643 unsigned int glyphs_length
,
644 hb_bool_t zero_context
)
646 if (unlikely (!hb_ot_shaper_face_data_ensure (face
))) return false;
647 return hb_ot_layout_lookup_would_substitute_fast (face
, lookup_index
, glyphs
, glyphs_length
, zero_context
);
651 hb_ot_layout_lookup_would_substitute_fast (hb_face_t
*face
,
652 unsigned int lookup_index
,
653 const hb_codepoint_t
*glyphs
,
654 unsigned int glyphs_length
,
655 hb_bool_t zero_context
)
657 if (unlikely (lookup_index
>= hb_ot_layout_from_face (face
)->gsub_lookup_count
)) return false;
658 OT::hb_would_apply_context_t
c (face
, glyphs
, glyphs_length
, zero_context
);
660 const OT::SubstLookup
& l
= hb_ot_layout_from_face (face
)->gsub
->get_lookup (lookup_index
);
662 return l
.would_apply (&c
, &hb_ot_layout_from_face (face
)->gsub_digests
[lookup_index
]);
666 hb_ot_layout_substitute_start (hb_font_t
*font
, hb_buffer_t
*buffer
)
668 OT::GSUB::substitute_start (font
, buffer
);
672 hb_ot_layout_substitute_lookup (hb_font_t
*font
,
674 unsigned int lookup_index
,
678 if (unlikely (lookup_index
>= hb_ot_layout_from_face (font
->face
)->gsub_lookup_count
)) return false;
680 OT::hb_apply_context_t
c (0, font
, buffer
, mask
, auto_zwj
);
682 const OT::SubstLookup
& l
= hb_ot_layout_from_face (font
->face
)->gsub
->get_lookup (lookup_index
);
684 return l
.apply_string (&c
, &hb_ot_layout_from_face (font
->face
)->gsub_digests
[lookup_index
]);
688 hb_ot_layout_substitute_finish (hb_font_t
*font
, hb_buffer_t
*buffer
)
690 OT::GSUB::substitute_finish (font
, buffer
);
694 hb_ot_layout_lookup_substitute_closure (hb_face_t
*face
,
695 unsigned int lookup_index
,
698 OT::hb_closure_context_t
c (face
, glyphs
);
700 const OT::SubstLookup
& l
= _get_gsub (face
).get_lookup (lookup_index
);
710 hb_ot_layout_has_positioning (hb_face_t
*face
)
712 return &_get_gpos (face
) != &OT::Null(OT::GPOS
);
716 hb_ot_layout_position_start (hb_font_t
*font
, hb_buffer_t
*buffer
)
718 OT::GPOS::position_start (font
, buffer
);
722 hb_ot_layout_position_lookup (hb_font_t
*font
,
724 unsigned int lookup_index
,
728 if (unlikely (lookup_index
>= hb_ot_layout_from_face (font
->face
)->gpos_lookup_count
)) return false;
730 OT::hb_apply_context_t
c (1, font
, buffer
, mask
, auto_zwj
);
732 const OT::PosLookup
& l
= hb_ot_layout_from_face (font
->face
)->gpos
->get_lookup (lookup_index
);
734 return l
.apply_string (&c
, &hb_ot_layout_from_face (font
->face
)->gpos_digests
[lookup_index
]);
738 hb_ot_layout_position_finish (hb_font_t
*font
, hb_buffer_t
*buffer
)
740 OT::GPOS::position_finish (font
, buffer
);
744 hb_ot_layout_get_size_params (hb_face_t
*face
,
745 unsigned int *design_size
, /* OUT. May be NULL */
746 unsigned int *subfamily_id
, /* OUT. May be NULL */
747 unsigned int *subfamily_name_id
, /* OUT. May be NULL */
748 unsigned int *range_start
, /* OUT. May be NULL */
749 unsigned int *range_end
/* OUT. May be NULL */)
751 const OT::GPOS
&gpos
= _get_gpos (face
);
752 const hb_tag_t tag
= HB_TAG ('s','i','z','e');
754 unsigned int num_features
= gpos
.get_feature_count ();
755 for (unsigned int i
= 0; i
< num_features
; i
++)
757 if (tag
== gpos
.get_feature_tag (i
))
759 const OT::Feature
&f
= gpos
.get_feature (i
);
760 const OT::FeatureParamsSize
¶ms
= f
.get_feature_params ().get_size_params (tag
);
762 if (params
.designSize
)
764 #define PARAM(a, A) if (a) *a = params.A
765 PARAM (design_size
, designSize
);
766 PARAM (subfamily_id
, subfamilyID
);
767 PARAM (subfamily_name_id
, subfamilyNameID
);
768 PARAM (range_start
, rangeStart
);
769 PARAM (range_end
, rangeEnd
);
777 #define PARAM(a, A) if (a) *a = 0
778 PARAM (design_size
, designSize
);
779 PARAM (subfamily_id
, subfamilyID
);
780 PARAM (subfamily_name_id
, subfamilyNameID
);
781 PARAM (range_start
, rangeStart
);
782 PARAM (range_end
, rangeEnd
);