277GlyphBufferAdvance Font::applyTransforms(GlyphBuffer& glyphBuffer, unsigned beginningGlyphIndex, unsigned beginningStringIndex, unsigned stringLength, bool enableKerning, bool requiresShaping, const AtomString& locale, StringView text, TextDirection textDirection) const
278{
279 glyphBuffer.remove(beginningGlyphIndex, glyphBuffer.size() - beginningGlyphIndex);
280
281 auto substring = text.substring(beginningStringIndex, stringLength);
282 auto upconvertedCharacters = substring.upconvertedCharacters();
283
284 Vector<SCRIPT_ITEM> items(6);
285 int numItems = 0;
286 for (;;) {
287 SCRIPT_CONTROL control { };
288 SCRIPT_STATE state { };
289 control.fMergeNeutralItems = true;
290 // Set up the correct direction for the run.
291 state.uBidiLevel = textDirection == TextDirection::RTL;
292 // Lock the correct directional override.
293 // state.fOverrideDirection = ?
294
295 // ScriptItemize may write (cMaxItems + 1) SCRIPT_ITEM.
296 HRESULT hr = ScriptItemize(wcharFrom(upconvertedCharacters.get()), stringLength, items.size() - 1, &control, &state, items.data(), &numItems);
297 if (hr != E_OUTOFMEMORY) {
298 ASSERT(SUCCEEDED(hr));
299 break;
300 }
301 items.resize(items.size() * 2);
302 }
303 items.resize(numItems + 1);
304
305 for (int i = 0; i < numItems; i++) {
306 // Determine the string for this item.
307 const UChar* str = upconvertedCharacters.get() + items[i].iCharPos;
308 int length = items[i+1].iCharPos - items[i].iCharPos;
309 SCRIPT_ITEM& item = items[i];
310
311 // Set up buffers to hold the results of shaping the item.
312 Vector<WORD> glyphs;
313 Vector<WORD> clusters;
314 Vector<SCRIPT_VISATTR> visualAttributes;
315 clusters.resize(length);
316
317 // Shape the item.
318 // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs.
319 // Apparently this is a good size to avoid having to make repeated calls to ScriptShape.
320 glyphs.resize(1.5 * length + 16);
321 visualAttributes.resize(glyphs.size());
322
323 if (!shapeByUniscribe(str, length, item, this, glyphs, clusters, visualAttributes))
324 continue;
325
326 // We now have a collection of glyphs.
327 Vector<GOFFSET> offsets;
328 Vector<int> advances;
329 offsets.resize(glyphs.size());
330 advances.resize(glyphs.size());
331 HRESULT placeResult = ScriptPlace(0, scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(),
332 &item.a, advances.data(), offsets.data(), 0);
333 if (placeResult == E_PENDING) {
334 // The script cache isn't primed with enough info yet. We need to select our HFONT into
335 // a DC and pass the DC in to ScriptPlace.
336 HWndDC hdc(0);
337 HFONT hfont = platformData().hfont();
338 HGDIOBJ oldFont = SelectObject(hdc, hfont);
339 placeResult = ScriptPlace(hdc, scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(),
340 &item.a, advances.data(), offsets.data(), 0);
341 SelectObject(hdc, oldFont);
342 }
343
344 if (FAILED(placeResult) || glyphs.isEmpty())
345 continue;
346
347 bool ltr = !item.a.fRTL;
348 auto stringIndices = stringIndicesFromClusters(clusters, StringView(str, length), beginningStringIndex + item.iCharPos, glyphs.size(), ltr);
349
350 for (unsigned k = 0; k < glyphs.size(); k++) {
351 const float cLogicalScale = platformData().useGDI() ? 1 : 32;
352 float advance = advances[k] / cLogicalScale;
353 float offsetX = offsets[k].du / cLogicalScale;
354 float offsetY = offsets[k].dv / cLogicalScale;
355
356 // Match AppKit's rules for the integer vs. non-integer rendering modes.
357 if (!platformData().isSystemFont()) {
358 advance = roundf(advance);
359 offsetX = roundf(offsetX);
360 offsetY = roundf(offsetY);
361 }
362
363 glyphBuffer.add(glyphs[k], *this, { advance, 0 }, stringIndices[k]);
364 glyphBuffer.origins(k)[0] = makeGlyphBufferOrigin(offsetX, offsetY);
365 }
366 }
367 return makeGlyphBufferAdvance();
368}
369