static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATSULineRef lineRef, URefCon refCon, void *operationExtraParameter, ATSULayoutOperationCallbackStatus *callbackStatus) { Q_UNUSED(selector); Q_UNUSED(operationExtraParameter); QGlyphLayoutInfo *nfo = reinterpret_cast<QGlyphLayoutInfo *>(refCon); nfo->callbackCalled = true; ATSLayoutRecord *layoutData = 0; ItemCount itemCount = 0; OSStatus e = noErr; e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, /*iCreate =*/ false, (void **) &layoutData, &itemCount); if (e != noErr) return e; *nfo->numGlyphs = itemCount - 1; Fixed *baselineDeltas = 0; e = ATSUDirectGetLayoutDataArrayPtrFromLineRef(lineRef, kATSUDirectDataBaselineDeltaFixedArray, /*iCreate =*/ true, (void **) &baselineDeltas, &itemCount); if (e != noErr) return e; int nextCharStop = -1; int currentClusterGlyph = -1; // first glyph in log cluster QShaperItem *item = 0; if (nfo->shaperItem) { item = nfo->shaperItem; #if !defined(QT_NO_DEBUG) int surrogates = 0; const QString &str = *item->string; for (int i = item->from; i < item->from + item->length - 1; ++i) { surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); } Q_ASSERT(*nfo->numGlyphs == item->length - surrogates); #endif for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop) if (item->charAttributes[nextCharStop].charStop) break; nextCharStop -= item->from; } nfo->glyphs[0].attributes.clusterStart = true; int glyphIdx = 0; int glyphIncrement = 1; if (nfo->flags & QTextEngine::RightToLeft) { glyphIdx = itemCount - 2; glyphIncrement = -1; } for (int i = 0; i < *nfo->numGlyphs; ++i, glyphIdx += glyphIncrement) { int charOffset = layoutData[glyphIdx].originalOffset / sizeof(UniChar); const int fontIdx = nfo->mappedFonts[charOffset]; ATSGlyphRef glyphId = layoutData[glyphIdx].glyphID; QFixed yAdvance = FixedToQFixed(baselineDeltas[glyphIdx]); QFixed xAdvance = FixedToQFixed(layoutData[glyphIdx + 1].realPos - layoutData[glyphIdx].realPos); if (glyphId != 0xffff || i == 0) { nfo->glyphs[i].glyph = (glyphId & 0x00ffffff) | (fontIdx << 24); nfo->glyphs[i].advance.y = yAdvance; nfo->glyphs[i].advance.x = xAdvance; } else { // ATSUI gives us 0xffff as glyph id at the index in the glyph array for // a character position that maps to a ligtature. Such a glyph id does not // result in any visual glyph, but it may have an advance, which is why we // sum up the glyph advances. --i; nfo->glyphs[i].advance.y += yAdvance; nfo->glyphs[i].advance.x += xAdvance; *nfo->numGlyphs -= 1; } if (item) { if (charOffset >= nextCharStop) { nfo->glyphs[i].attributes.clusterStart = true; currentClusterGlyph = i; ++nextCharStop; for (; nextCharStop < item->length; ++nextCharStop) if (item->charAttributes[item->from + nextCharStop].charStop) break; } else { if (currentClusterGlyph == -1) currentClusterGlyph = i; } item->log_clusters[charOffset] = currentClusterGlyph; // surrogate handling if (charOffset < item->length - 1) { QChar current = item->string->at(item->from + charOffset); QChar next = item->string->at(item->from + charOffset + 1); if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00 && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) { item->log_clusters[charOffset + 1] = currentClusterGlyph; } } } } /* if (item) { qDebug() << "resulting logclusters:"; for (int i = 0; i < item->length; ++i) qDebug() << "logClusters[" << i << "] =" << item->log_clusters[i]; qDebug() << "clusterstarts:"; for (int i = 0; i < *nfo->numGlyphs; ++i) qDebug() << "clusterStart[" << i << "] =" << nfo->glyphs[i].attributes.clusterStart; } */ ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataBaselineDeltaFixedArray, (void **) &baselineDeltas); ATSUDirectReleaseLayoutDataArrayPtr(lineRef, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **) &layoutData); *callbackStatus = kATSULayoutOperationCallbackStatusHandled; return noErr; }
static cairo_status_t _cairo_atsui_font_text_to_glyphs(void *abstract_font, const char *utf8, cairo_glyph_t **glyphs, int *num_glyphs) { cairo_atsui_font_t *font = abstract_font; size_t i; OSStatus err; ATSUTextLayout textLayout; ATSLayoutRecord *layoutRecords; ItemCount glyphCount; int charCount; UniChar *theText; cairo_status_t status; // liberal estimate of size charCount = strlen(utf8); if (charCount == 0) { *glyphs = NULL; *num_glyphs = 0; return CAIRO_STATUS_SUCCESS; } status = _cairo_utf8_to_utf16 (utf8, -1, &theText, &charCount); if (status) return status; err = ATSUCreateTextLayout(&textLayout); err = ATSUSetTextPointerLocation(textLayout, theText, 0, charCount, charCount); // Set the style for all of the text err = ATSUSetRunStyle(textLayout, font->unscaled_style, kATSUFromTextBeginning, kATSUToTextEnd); // Get the glyphs from the text layout object err = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(textLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void *) &layoutRecords, &glyphCount); *num_glyphs = glyphCount - 1; *glyphs = (cairo_glyph_t *) malloc(*num_glyphs * (sizeof(cairo_glyph_t))); if (*glyphs == NULL) { return CAIRO_STATUS_NO_MEMORY; } for (i = 0; i < *num_glyphs; i++) { (*glyphs)[i].index = layoutRecords[i].glyphID; (*glyphs)[i].x = FixedToFloat(layoutRecords[i].realPos); (*glyphs)[i].y = 0; } free(theText); ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void *) &layoutRecords); ATSUDisposeTextLayout(textLayout); return CAIRO_STATUS_SUCCESS; }
void DoAtsuiLayout(void* p, int justify) { memoryword* node = (memoryword*)p; unsigned f = native_font(node); if (fontarea[f] != AAT_FONT_FLAG) { fprintf(stderr, "internal error: do_atsui_layout called for non-ATSUI font\n"); exit(1); } if (sTextLayout == 0) InitializeLayout(); OSStatus status = noErr; long txtLen = native_length(node); const UniChar* txtPtr = (UniChar*)(node + native_node_size); status = ATSUSetTextPointerLocation(sTextLayout, txtPtr, 0, txtLen, txtLen); // we're using this font in AAT mode, so fontlayoutengine[f] is actually an ATSUStyle ATSUStyle style = (ATSUStyle)(fontlayoutengine[native_font(node)]); status = ATSUSetRunStyle(sTextLayout, style, 0, txtLen); ATSUAttributeTag tags[] = { kATSULineWidthTag, kATSULineJustificationFactorTag }; ItemCount numTags = sizeof(tags) / sizeof(ATSUAttributeTag); if (justify) { ByteCount valSizes[] = { sizeof(Fixed), sizeof(Fract) }; Fixed wid = FixedTeXtoPSPoints(node_width(node)); Fract just = fract1; ATSUAttributeValuePtr valPtrs[] = { &wid, &just }; status = ATSUSetLayoutControls(sTextLayout, numTags, tags, valSizes, valPtrs); } ItemCount count; ATSLayoutRecord* layoutRec = NULL; status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(sTextLayout, 0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void*)&layoutRec, &count); int i; int realGlyphCount = 0; int lastRealGlyph = 0; for (i = 0; i < count; ++i) if (layoutRec[i].glyphID < 0xfffe) { lastRealGlyph = i; ++realGlyphCount; } void* glyph_info = xmalloc(realGlyphCount * native_glyph_info_size); FixedPoint* locations = (FixedPoint*)glyph_info; UInt16* glyphIDs = (UInt16*)(locations + realGlyphCount); Fixed lsUnit = justify ? 0 : fontletterspace[f]; Fixed lsDelta = 0; realGlyphCount = 0; for (i = 0; i < count; ++i) { if (layoutRec[i].glyphID < 0xfffe) { if ((layoutRec[i].flags & kATSGlyphInfoIsAttachment) && (lsDelta != 0)) lsDelta -= lsUnit; glyphIDs[realGlyphCount] = layoutRec[i].glyphID; locations[realGlyphCount].y = 0; /* FIXME: won't handle baseline offsets */ locations[realGlyphCount].x = FixedPStoTeXPoints(layoutRec[i].realPos) + lsDelta; lsDelta += lsUnit; ++realGlyphCount; } } if (lsDelta != 0) lsDelta -= lsUnit; native_glyph_count(node) = realGlyphCount; native_glyph_info_ptr(node) = glyph_info; if (!justify) node_width(node) = FixedPStoTeXPoints(layoutRec[count-1].realPos) + lsDelta; ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void*)&layoutRec); if (justify) ATSUClearLayoutControls(sTextLayout, numTags, tags); }