2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * This is part of HarfBuzz, an OpenType Layout engine library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
28 #include "harfbuzz-stream-private.h"
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
35 // --------------------------------------------------------------------------------------------------------------------------------------------
39 // --------------------------------------------------------------------------------------------------------------------------------------------
41 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
43 int nmarks = glast - gfrom;
46 HB_Glyph *glyphs = item->glyphs;
47 HB_GlyphAttributes *attributes = item->attributes;
49 HB_GlyphMetrics baseMetrics;
50 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
52 if (item->item.script == HB_Script_Hebrew
53 && (-baseMetrics.y) > baseMetrics.height)
54 // we need to attach below the baseline, because of the hebrew iud.
55 baseMetrics.height = -baseMetrics.y;
57 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
58 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
60 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
61 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
62 if (size > HB_FIXED_CONSTANT(4))
63 offsetBase += HB_FIXED_CONSTANT(4);
66 offsetBase = -offsetBase;
67 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
68 // qDebug("offset = %f", offsetBase);
70 bool rightToLeft = item->item.bidiLevel % 2;
73 unsigned char lastCmb = 0;
74 HB_GlyphMetrics attachmentRect;
75 memset(&attachmentRect, 0, sizeof(attachmentRect));
77 for(i = 1; i <= nmarks; i++) {
78 HB_Glyph mark = glyphs[gfrom+i];
79 HB_GlyphMetrics markMetrics;
80 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
83 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
85 HB_Fixed offset = offsetBase;
86 unsigned char cmb = attributes[gfrom+i].combiningClass;
88 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
89 // bits in the glyphAttributes structure.
91 // fixed position classes. We approximate by mapping to one of the others.
92 // currently I added only the ones for arabic, hebrew, lao and thai.
94 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
96 // add a bit more offset to arabic, a bit hacky
97 if (cmb >= 27 && cmb <= 36 && offset < 3)
100 if ((cmb >= 10 && cmb <= 18) ||
101 cmb == 20 || cmb == 22 ||
102 cmb == 29 || cmb == 32)
103 cmb = HB_Combining_Below;
105 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
106 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
107 cmb = HB_Combining_Above;
109 else if (cmb == 9 || cmb == 103 || cmb == 118)
110 cmb = HB_Combining_BelowRight;
112 else if (cmb == 24 || cmb == 107 || cmb == 122)
113 cmb = HB_Combining_AboveRight;
115 cmb = HB_Combining_AboveLeft;
121 // combining marks of different class don't interact. Reset the rectangle.
122 if (cmb != lastCmb) {
123 //qDebug("resetting rect");
124 attachmentRect = baseMetrics;
128 case HB_Combining_DoubleBelow:
129 // ### wrong in rtl context!
130 case HB_Combining_BelowLeft:
132 case HB_Combining_BelowLeftAttached:
133 p.x += attachmentRect.x - markMetrics.x;
134 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
136 case HB_Combining_Below:
138 case HB_Combining_BelowAttached:
139 p.x += attachmentRect.x - markMetrics.x;
140 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
142 p.x += (attachmentRect.width - markMetrics.width) / 2;
144 case HB_Combining_BelowRight:
146 case HB_Combining_BelowRightAttached:
147 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
148 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
150 case HB_Combining_Left:
152 case HB_Combining_LeftAttached:
154 case HB_Combining_Right:
156 case HB_Combining_RightAttached:
158 case HB_Combining_DoubleAbove:
159 // ### wrong in RTL context!
160 case HB_Combining_AboveLeft:
162 case HB_Combining_AboveLeftAttached:
163 p.x += attachmentRect.x - markMetrics.x;
164 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
166 case HB_Combining_Above:
168 case HB_Combining_AboveAttached:
169 p.x += attachmentRect.x - markMetrics.x;
170 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
172 p.x += (attachmentRect.width - markMetrics.width) / 2;
174 case HB_Combining_AboveRight:
176 case HB_Combining_AboveRightAttached:
177 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
178 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
181 case HB_Combining_IotaSubscript:
185 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
186 markMetrics.x += p.x;
187 markMetrics.y += p.y;
189 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
190 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
191 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
192 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
193 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
194 attachmentRect = unitedAttachmentRect;
198 item->offsets[gfrom+i].x = p.x;
199 item->offsets[gfrom+i].y = p.y;
201 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
202 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
204 item->advances[gfrom+i] = 0;
208 void HB_HeuristicPosition(HB_ShaperItem *item)
210 HB_GetGlyphAdvances(item);
211 HB_GlyphAttributes *attributes = item->attributes;
214 int i = item->num_glyphs;
216 if (cEnd == -1 && attributes[i].mark) {
218 } else if (cEnd != -1 && !attributes[i].mark) {
219 positionCluster(item, i, cEnd);
225 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
226 // and no reordering.
227 // also computes logClusters heuristically
228 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
230 const HB_UChar16 *uc = item->string + item->item.pos;
231 hb_uint32 length = item->item.length;
233 // ### zeroWidth and justification are missing here!!!!!
235 assert(item->num_glyphs <= length);
237 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
238 HB_GlyphAttributes *attributes = item->attributes;
239 unsigned short *logClusters = item->log_clusters;
241 hb_uint32 glyph_pos = 0;
243 for (i = 0; i < length; i++) {
244 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
245 && HB_IsLowSurrogate(uc[i + 1])) {
246 logClusters[i] = glyph_pos;
247 logClusters[++i] = glyph_pos;
249 logClusters[i] = glyph_pos;
253 assert(glyph_pos == item->num_glyphs);
255 // first char in a run is never (treated as) a mark
257 const bool symbolFont = item->face->isSymbolFont;
258 attributes[0].mark = false;
259 attributes[0].clusterStart = true;
260 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
263 HB_CharCategory lastCat;
265 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
266 for (i = 1; i < length; ++i) {
267 if (logClusters[i] == pos)
271 while (pos < logClusters[i]) {
272 attributes[pos] = attributes[pos-1];
275 // hide soft-hyphens by default
276 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
277 attributes[pos].dontPrint = true;
280 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
281 if (cat != HB_Mark_NonSpacing) {
282 attributes[pos].mark = false;
283 attributes[pos].clusterStart = true;
284 attributes[pos].combiningClass = 0;
285 cStart = logClusters[i];
288 // Fix 0 combining classes
289 if ((uc[pos] & 0xff00) == 0x0e00) {
291 if (uc[pos] == 0xe31 ||
300 cmb = HB_Combining_AboveRight;
301 } else if (uc[pos] == 0xeb1 ||
309 cmb = HB_Combining_Above;
310 } else if (uc[pos] == 0xebc) {
311 cmb = HB_Combining_Below;
316 attributes[pos].mark = true;
317 attributes[pos].clusterStart = false;
318 attributes[pos].combiningClass = cmb;
319 logClusters[i] = cStart;
321 // one gets an inter character justification point if the current char is not a non spacing mark.
322 // as then the current char belongs to the last one and one gets a space justification point
323 // after the space char.
324 if (lastCat == HB_Separator_Space)
325 attributes[pos-1].justification = HB_Space;
326 else if (cat != HB_Mark_NonSpacing)
327 attributes[pos-1].justification = HB_Character;
329 attributes[pos-1].justification = HB_NoJustification;
333 pos = logClusters[length-1];
334 if (lastCat == HB_Separator_Space)
335 attributes[pos].justification = HB_Space;
337 attributes[pos].justification = HB_Character;
341 static const HB_OpenTypeFeature basic_features[] = {
342 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
343 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
344 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
349 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
351 if (shaper_item->glyphIndicesPresent) {
352 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
353 shaper_item->glyphIndicesPresent = false;
356 return shaper_item->font->klass
357 ->convertStringToGlyphIndices(shaper_item->font,
358 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
359 shaper_item->glyphs, &shaper_item->num_glyphs,
360 shaper_item->item.bidiLevel % 2);
363 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
366 const int availableGlyphs = shaper_item->num_glyphs;
369 if (!HB_ConvertStringToGlyphIndices(shaper_item))
372 HB_HeuristicSetGlyphAttributes(shaper_item);
375 if (HB_SelectScript(shaper_item, basic_features)) {
376 HB_OpenTypeShape(shaper_item, /*properties*/0);
377 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
381 HB_HeuristicPosition(shaper_item);
385 const HB_ScriptEngine HB_ScriptEngines[] = {
445 static inline char *tag_to_string(HB_UInt tag)
447 static char string[5];
448 string[0] = (tag >> 24)&0xff;
449 string[1] = (tag >> 16)&0xff;
450 string[2] = (tag >> 8)&0xff;
451 string[3] = tag&0xff;
457 static void dump_string(HB_Buffer buffer)
459 for (uint i = 0; i < buffer->in_length; ++i) {
460 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
465 #define DEBUG if (1) ; else printf
469 #define DefaultLangSys 0xffff
470 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
482 static const OTScripts ot_scripts [] = {
484 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
486 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
488 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
490 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
492 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
494 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
496 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
498 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
500 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
502 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
504 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
506 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
508 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
510 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
512 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
514 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
516 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
518 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
520 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
522 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
524 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
526 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
528 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
530 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
532 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
534 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
536 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
538 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
540 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
542 static HB_Bool checkScript(HB_Face face, int script)
544 assert(script < HB_ScriptCount);
546 if (!face->gsub && !face->gpos)
549 unsigned int tag = ot_scripts[script].tag;
550 int requirements = ot_scripts[script].flags;
552 if (requirements & RequiresGsub) {
556 HB_UShort script_index;
557 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
559 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
560 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
566 if (requirements & RequiresGpos) {
570 HB_UShort script_index;
571 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
573 DEBUG("could not select script in gpos table: %d", error);
574 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
583 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
587 HB_Stream stream = 0;
592 error = tableFunc(font, tag, 0, &length);
595 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
598 stream->base = (HB_Byte*)malloc(length);
603 error = tableFunc(font, tag, stream->base, &length);
605 _hb_close_stream(stream);
608 stream->size = length;
610 stream->cursor = NULL;
614 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
616 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
620 face->isSymbolFont = false;
624 face->current_script = HB_ScriptCount;
625 face->current_flags = HB_ShaperFlag_Default;
626 face->has_opentype_kerning = false;
627 face->tmpAttributes = 0;
628 face->tmpLogClusters = 0;
629 face->glyphs_substituted = false;
632 HB_Error error = HB_Err_Ok;
634 HB_Stream gdefStream;
636 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
637 error = HB_Err_Not_Covered;
638 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
639 //DEBUG("error loading gdef table: %d", error);
643 //DEBUG() << "trying to load gsub table";
644 stream = getTableStream(font, tableFunc, TTAG_GSUB);
645 error = HB_Err_Not_Covered;
646 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
648 if (error != HB_Err_Not_Covered) {
649 //DEBUG("error loading gsub table: %d", error);
651 //DEBUG("face doesn't have a gsub table");
654 _hb_close_stream(stream);
656 stream = getTableStream(font, tableFunc, TTAG_GPOS);
657 error = HB_Err_Not_Covered;
658 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
660 DEBUG("error loading gpos table: %d", error);
662 _hb_close_stream(stream);
664 _hb_close_stream(gdefStream);
666 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
667 face->supported_scripts[i] = checkScript(face, i);
669 if (HB_Buffer_new(&face->buffer) != HB_Err_Ok) {
677 void HB_FreeFace(HB_Face face)
682 HB_Done_GPOS_Table(face->gpos);
684 HB_Done_GSUB_Table(face->gsub);
686 HB_Done_GDEF_Table(face->gdef);
688 HB_Buffer_free(face->buffer);
689 if (face->tmpAttributes)
690 free(face->tmpAttributes);
691 if (face->tmpLogClusters)
692 free(face->tmpLogClusters);
696 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
698 HB_Script script = shaper_item->item.script;
700 if (!shaper_item->face->supported_scripts[script])
703 HB_Face face = shaper_item->face;
704 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
707 face->current_script = script;
708 face->current_flags = shaper_item->shaperFlags;
710 assert(script < HB_ScriptCount);
711 // find script in our list of supported scripts.
712 unsigned int tag = ot_scripts[script].tag;
714 if (face->gsub && features) {
717 HB_FeatureList featurelist = face->gsub->FeatureList;
718 int numfeatures = featurelist.FeatureCount;
719 DEBUG("gsub table has %d features", numfeatures);
720 for (int i = 0; i < numfeatures; i++) {
721 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
722 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
726 HB_GSUB_Clear_Features(face->gsub);
727 HB_UShort script_index;
728 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
730 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
731 while (features->tag) {
732 HB_UShort feature_index;
733 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
735 DEBUG(" adding feature %s", tag_to_string(features->tag));
736 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
744 face->has_opentype_kerning = false;
747 HB_GPOS_Clear_Features(face->gpos);
748 HB_UShort script_index;
749 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
753 HB_FeatureList featurelist = face->gpos->FeatureList;
754 int numfeatures = featurelist.FeatureCount;
755 DEBUG("gpos table has %d features", numfeatures);
756 for(int i = 0; i < numfeatures; i++) {
757 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
758 HB_UShort feature_index;
759 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
760 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
764 HB_UInt *feature_tag_list_buffer;
765 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
767 HB_UInt *feature_tag_list = feature_tag_list_buffer;
768 while (*feature_tag_list) {
769 HB_UShort feature_index;
770 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
771 if (face->current_flags & HB_ShaperFlag_NoKerning) {
775 face->has_opentype_kerning = true;
777 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
779 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
782 FREE(feature_tag_list_buffer);
790 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
792 HB_GlyphAttributes *tmpAttributes;
793 unsigned int *tmpLogClusters;
795 HB_Face face = item->face;
797 face->length = item->num_glyphs;
799 HB_Buffer_clear(face->buffer);
801 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
804 face->tmpAttributes = tmpAttributes;
806 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
809 face->tmpLogClusters = tmpLogClusters;
811 for (int i = 0; i < face->length; ++i) {
812 HB_Buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
813 face->tmpAttributes[i] = item->attributes[i];
814 face->tmpLogClusters[i] = item->log_clusters[i];
818 DEBUG("-----------------------------------------");
819 // DEBUG("log clusters before shaping:");
820 // for (int j = 0; j < length; j++)
821 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
822 DEBUG("original glyphs: %p", item->glyphs);
823 for (int i = 0; i < length; ++i)
824 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
825 // dump_string(hb_buffer);
828 face->glyphs_substituted = false;
830 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
831 if (error && error != HB_Err_Not_Covered)
833 face->glyphs_substituted = (error != HB_Err_Not_Covered);
837 // DEBUG("log clusters before shaping:");
838 // for (int j = 0; j < length; j++)
839 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
840 DEBUG("shaped glyphs:");
841 for (int i = 0; i < length; ++i)
842 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
843 DEBUG("-----------------------------------------");
844 // dump_string(hb_buffer);
850 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
852 HB_Face face = item->face;
854 bool glyphs_positioned = false;
856 if (face->buffer->positions)
857 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
858 // #### check that passing "false,false" is correct
859 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
862 if (!face->glyphs_substituted && !glyphs_positioned) {
863 HB_GetGlyphAdvances(item);
864 return true; // nothing to do for us
867 // make sure we have enough space to write everything back
868 if (availableGlyphs < (int)face->buffer->in_length) {
869 item->num_glyphs = face->buffer->in_length;
873 HB_Glyph *glyphs = item->glyphs;
874 HB_GlyphAttributes *attributes = item->attributes;
876 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
877 glyphs[i] = face->buffer->in_string[i].gindex;
878 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
879 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
880 attributes[i].clusterStart = false;
882 item->num_glyphs = face->buffer->in_length;
884 if (doLogClusters && face->glyphs_substituted) {
885 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
886 unsigned short *logClusters = item->log_clusters;
887 int clusterStart = 0;
889 // #### the reconstruction of the logclusters currently does not work if the original string
890 // contains surrogate pairs
891 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
892 int ci = face->buffer->in_string[i].cluster;
893 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
894 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
895 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
896 for (int j = oldCi; j < ci; j++)
897 logClusters[j] = clusterStart;
902 for (int j = oldCi; j < face->length; j++)
903 logClusters[j] = clusterStart;
906 // calulate the advances for the shaped glyphs
907 // DEBUG("unpositioned: ");
910 if (glyphs_positioned) {
911 HB_GetGlyphAdvances(item);
912 HB_Position positions = face->buffer->positions;
913 HB_Fixed *advances = item->advances;
915 // DEBUG("positioned glyphs:");
916 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
917 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
918 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
919 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
920 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
921 // positions[i].back, positions[i].new_advance);
923 HB_Fixed adjustment = positions[i].x_advance;
925 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
926 adjustment = HB_FIXED_ROUND(adjustment);
928 if (positions[i].new_advance) {
929 ; //advances[i] = adjustment;
931 advances[i] += adjustment;
935 HB_FixedPoint *offsets = item->offsets;
936 offsets[i].x = positions[i].x_pos;
937 offsets[i].y = positions[i].y_pos;
938 while (positions[i - back].back) {
939 back += positions[i - back].back;
940 offsets[i].x += positions[i - back].x_pos;
941 offsets[i].y += positions[i - back].y_pos;
943 offsets[i].y = -offsets[i].y;
945 if (item->item.bidiLevel % 2) {
946 // ### may need to go back multiple glyphs like in ltr
947 back = positions[i].back;
949 offsets[i].x -= advances[i-back];
952 while (positions[i - back].back) {
953 back += positions[i - back].back;
954 offsets[i].x -= advances[i-back];
957 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
958 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
960 item->kerning_applied = face->has_opentype_kerning;
962 HB_HeuristicPosition(item);
967 DEBUG("log clusters after shaping:");
968 for (int j = 0; j < length; j++)
969 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
971 DEBUG("final glyphs:");
972 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
973 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
974 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
975 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
976 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
977 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
978 DEBUG("-----------------------------------------");
983 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
985 HB_Bool result = false;
986 if (shaper_item->num_glyphs < shaper_item->item.length) {
987 shaper_item->num_glyphs = shaper_item->item.length;
990 assert(shaper_item->item.script < HB_ScriptCount);
991 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
992 shaper_item->glyphIndicesPresent = false;