void SHAPE2::compute(DspBuffer& dspbuf) //final { float pad = _dbd._inputPad; int inumframes = dspbuf._numframes; float* ubuf = dspbuf.channel(0); float amt = _param[0].eval(); _fval[0] = amt; if(1) for( int i=0; i<inumframes; i++ ) { float s1 = shaper(ubuf[i]*pad,amt); float s2 = shaper(s1,amt*0.75); ubuf[i] = s2; } }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const { HarfBuzzShaper shaper(this, run); if (!shaper.shape()) return 0; return shaper.totalWidth(); }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { #if PLATFORM(CHROMIUM) if (preferHarfBuzz(this)) { GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); shaper.setDrawRange(from, to); if (shaper.shape(&glyphBuffer)) { drawGlyphBuffer(context, run, glyphBuffer, point); return; } } #endif // This glyph buffer holds our glyphs + advances + font data for each glyph. GlyphBuffer glyphBuffer; float startX = point.x() + getGlyphsAndAdvancesForComplexText(run, from, to, glyphBuffer); // We couldn't generate any glyphs for the run. Give up. if (glyphBuffer.isEmpty()) return; // Draw the glyph buffer now at the starting point returned in startX. FloatPoint startPoint(startX, point.y()); drawGlyphBuffer(context, run, glyphBuffer, startPoint); }
float Font::getGlyphsAndAdvancesForComplexText(const TextRunPaintInfo& runInfo, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const { HarfBuzzShaper shaper(this, runInfo.run, HarfBuzzShaper::ForTextEmphasis); shaper.setDrawRange(runInfo.from, runInfo.to); shaper.shape(&glyphBuffer); return 0; }
TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabicThaiHanLatin) { UChar mixedString[] = {0x628, 0x64A, 0x629, 0xE20, 0x65E5, 0x62}; TextRun mixed(mixedString, 6); HarfBuzzShaper shaper(&font, mixed); RefPtr<ShapeResult> result = shaper.shapeResult(); ASSERT_EQ(4u, testInfo(result)->numberOfRunsForTesting()); ASSERT_TRUE( testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_ARABIC, script); ASSERT_TRUE( testInfo(result)->runInfoForTesting(1, startIndex, numGlyphs, script)); EXPECT_EQ(3u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_THAI, script); ASSERT_TRUE( testInfo(result)->runInfoForTesting(2, startIndex, numGlyphs, script)); EXPECT_EQ(4u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_HAN, script); ASSERT_TRUE( testInfo(result)->runInfoForTesting(3, startIndex, numGlyphs, script)); EXPECT_EQ(5u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); }
// Tests that filling a glyph buffer for a specific range returns the same // results when shaping word by word as when shaping the full run in one go. TEST_F(CachingWordShaperTest, CommonAccentRightToLeftFillGlyphBuffer) { // "[] []" with an accent mark over the last square bracket. const UChar str[] = { 0x5B, 0x5D, 0x20, 0x5B, 0x301, 0x5D, 0x0 }; TextRun textRun(str, 6); textRun.setDirection(RTL); CachingWordShaper shaper(cache.get()); GlyphBuffer glyphBuffer; shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 1, 6); OwnPtr<ShapeCache> referenceCache = adoptPtr(new ShapeCache()); CachingWordShaper referenceShaper(referenceCache.get()); GlyphBuffer referenceGlyphBuffer; font.setCanShapeWordByWordForTesting(false); referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &referenceGlyphBuffer, 1, 6); ASSERT_EQ(5u, referenceGlyphBuffer.size()); ASSERT_EQ(referenceGlyphBuffer.size(), glyphBuffer.size()); ASSERT_EQ(referenceGlyphBuffer.glyphAt(0), glyphBuffer.glyphAt(0)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(1), glyphBuffer.glyphAt(1)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(2), glyphBuffer.glyphAt(2)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(3), glyphBuffer.glyphAt(3)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(4), glyphBuffer.glyphAt(4)); }
void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, const FloatPoint& point, int from, int to) const { if (!run.length()) return; TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); bool fill = textMode & TextModeFill; bool stroke = (textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0; if (!fill && !stroke) return; SkPaint strokePaint, fillPaint; if (fill) { gc->platformContext()->setupPaintForFilling(&fillPaint); setupForTextPainting(&fillPaint, gc->fillColor().rgb()); } if (stroke) { gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); } GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); shaper.setDrawRange(from, to); if (!shaper.shape(&glyphBuffer)) return; FloatPoint adjustedPoint = shaper.adjustStartPoint(point); drawGlyphBuffer(gc, run, glyphBuffer, adjustedPoint); }
TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsDevanagariCommonLatinCommon) { UChar devanagariCommonLatinString[] = {0x915, 0x94d, 0x930, 0x20, 0x61, 0x62, 0x2E}; TextRun devanagariCommonLatin(devanagariCommonLatinString, 7); HarfBuzzShaper shaper(&font, devanagariCommonLatin); RefPtr<ShapeResult> result = shaper.shapeResult(); ASSERT_EQ(3u, testInfo(result)->numberOfRunsForTesting()); ASSERT_TRUE( testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); ASSERT_TRUE( testInfo(result)->runInfoForTesting(1, startIndex, numGlyphs, script)); EXPECT_EQ(3u, startIndex); EXPECT_EQ(1u, numGlyphs); EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script); ASSERT_TRUE( testInfo(result)->runInfoForTesting(2, startIndex, numGlyphs, script)); EXPECT_EQ(4u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int height, int from, int to) const { HarfBuzzShaper shaper(this, run); if (!shaper.shape()) return FloatRect(); return shaper.selectionRect(point, height, from, to); }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const { HarfBuzzShaper shaper(this, run); if (shaper.shape()) return shaper.totalWidth(); LOG_ERROR("Shaper couldn't shape text run."); return 0; }
int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool) const { HarfBuzzShaper shaper(this, run); if (shaper.shape()) return shaper.offsetForPosition(x); LOG_ERROR("Shaper couldn't shape text run."); return 0; }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { HarfBuzzShaper shaper(this, run); if (shaper.shape()) return shaper.selectionRect(point, h, from, to); LOG_ERROR("Shaper couldn't shape text run."); return FloatRect(); }
// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const { HarfBuzzShaper shaper(this, run); if (!shaper.shape()) return 0; return shaper.offsetForPosition(xFloat); }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int, int) const { GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); if (shaper.shape(&glyphBuffer)) drawGlyphBuffer(context, run, glyphBuffer, point); else LOG_ERROR("Shaper couldn't shape glyphBuffer."); }
ade::TensptrT grad_max (ade::iFunctor* fwd, size_t gradidx, ade::TensT tens) { ade::TensptrT fwd_cpy(ade::Functor::get( fwd->get_opcode(), fwd->get_children())); auto fchildren = fwd->get_children(); ade::CoordptrT shaper(fchildren[gradidx].get_shaper()->reverse()); ade::TensptrT rev_fwd(ade::Functor::get(ade::Opcode{"SUM",age::SUM}, {ade::FuncArg(fwd_cpy, shaper)})); return age::eq(rev_fwd, tens[gradidx]); }
int Font::offsetForPositionForComplexText(const TextRun& run, float x, bool includePartialGlyphs) const { if (preferHarfBuzz(this)) { HarfBuzzShaper shaper(this, run); if (shaper.shape()) return shaper.offsetForPosition(x); } ComplexTextController controller(this, run); return controller.offsetForPosition(x, includePartialGlyphs); }
// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const { // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. int targetX = static_cast<int>(position); HarfBuzzShaper shaper(this, run); if (!shaper.shape()) return 0; return shaper.offsetForPosition(targetX); }
TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLeadingCommon) { TextRun leadingCommon(reinterpret_cast<const LChar*>("... test"), 8); HarfBuzzShaper shaper(&font, leadingCommon); RefPtr<ShapeResult> result = shaper.shapeResult(); ASSERT_EQ(1u, testInfo(result)->numberOfRunsForTesting()); ASSERT_TRUE( testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(8u, numGlyphs); EXPECT_EQ(HB_SCRIPT_LATIN, script); }
float Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int, int) const { GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); if (shaper.shape(&glyphBuffer)) { FloatPoint startPoint = point; float startX = startPoint.x(); drawGlyphBuffer(context, run, glyphBuffer, startPoint); return startPoint.x() - startX; } LOG_ERROR("Shaper couldn't shape glyphBuffer."); return 0; }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, IntRectExtent* glyphBounds) const { HarfBuzzShaper shaper(this, run, HarfBuzzShaper::NotForTextEmphasis, fallbackFonts); if (!shaper.shape()) return 0; glyphBounds->setTop(floorf(-shaper.glyphBoundingBox().top())); glyphBounds->setBottom(ceilf(shaper.glyphBoundingBox().bottom())); glyphBounds->setLeft(std::max<int>(0, floorf(-shaper.glyphBoundingBox().left()))); glyphBounds->setRight(std::max<int>(0, ceilf(shaper.glyphBoundingBox().right() - shaper.totalWidth()))); return shaper.totalWidth(); }
TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabic) { UChar arabicString[] = {0x628, 0x64A, 0x629}; TextRun arabic(arabicString, 3); HarfBuzzShaper shaper(&font, arabic); RefPtr<ShapeResult> result = shaper.shapeResult(); ASSERT_EQ(1u, testInfo(result)->numberOfRunsForTesting()); ASSERT_TRUE( testInfo(result)->runInfoForTesting(0, startIndex, numGlyphs, script)); EXPECT_EQ(0u, startIndex); EXPECT_EQ(3u, numGlyphs); EXPECT_EQ(HB_SCRIPT_ARABIC, script); }
/** Use HarfBuzz to shape segment in question */ void shapeGlyphs(StoryText *itemText, int startIndex, int endIndex) { if(startIndex < 0 || endIndex < 1) { return; } QString text = itemText->text(startIndex, endIndex-startIndex); //this one takes pos, len ScText * scitem = itemText->item(startIndex); ScFace scface = scitem->font(); qreal size = scitem->fontSize(); ShaperFontInfo font(scface, size / 10.0); ShaperItemInfo shaper(&font); ShaperOutput out = shaper.shapeItem(text); //HarfBuzz Magic //out.glyphs has our glyphs for(uint i = 0; i < out.num_glyphs; i++) { GlyphLayout &glyph = itemText->item(startIndex + i)->glyph; glyph.glyph = out.glyphs[i]; // XXX: I can't figure out how to use the advances returned by harfbuzz // to position the glyphs, so I'm going to ask the scribus font // class to get the advances for me, then I'll handle marks as // a special case where there's no advance // // Please somebody FIXME // glyph.xadvance = scface.glyphWidth(glyph.glyph, size); //works .. sorta!! if(out.attributes[i].mark) //HACK vowel marks { glyph.xadvance = 0; } //debugging // qDebug() << "glyphs[" << i << "] =" << out.glyphs[i]; // qDebug() << "advance[" << i << "] =" << out.advances[i]; } if(out.num_glyphs < (uint)text.length()) { // There were some ligatures, so zero-out the extra characters at the end of this run for(int i = out.num_glyphs; i < text.length(); i++) { GlyphLayout &glyph = itemText->item(startIndex + i)->glyph; glyph.glyph = scface.char2CMap(QChar::Nbsp); glyph.xadvance = 0; } } }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { if (!run.length()) return; GlyphBuffer glyphBuffer; HarfBuzzShaper shaper(this, run); shaper.setDrawRange(from, to); if (!shaper.shape(&glyphBuffer)) return; FloatPoint adjustedPoint = shaper.adjustStartPoint(point); drawGlyphBuffer(context, run, glyphBuffer, adjustedPoint); }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { if (preferHarfBuzz(this)) { HarfBuzzShaper shaper(this, run); if (shaper.shape()) return shaper.totalWidth(); } ComplexTextController controller(this, run, true, fallbackFonts); if (glyphOverflow) { glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent())); glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent())); glyphOverflow->left = max<int>(0, ceilf(-controller.minGlyphBoundingBoxX())); glyphOverflow->right = max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.totalWidth())); } return controller.totalWidth(); }
// Tests that runs with zero glyphs (the ZWJ non-printable character in this // case) are handled correctly. This test passes if it does not cause a crash. TEST_F(CachingWordShaperTest, SubRunWithZeroGlyphs) { // "Foo ‌ bar" const UChar str[] = {0x46, 0x6F, 0x6F, 0x20, 0x200C, 0x20, 0x62, 0x61, 0x71, 0x0 }; TextRun textRun(str, 9); CachingWordShaper shaper(cache.get()); FloatRect glyphBounds; ASSERT_GT(shaper.width(&font, textRun, nullptr, &glyphBounds), 0); GlyphBuffer glyphBuffer; shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 0, 8); shaper.getCharacterRange(&font, textRun, 0, 8); }
void SHAPER::compute(DspBuffer& dspbuf) //final { float pad = _dbd._inputPad; int inumframes = dspbuf._numframes; float amt = _param[0].eval();//,0.01f,100.0f); _fval[0] = amt; float* inpbuf = getInpBuf1(dspbuf); //float la = decibel_to_linear_amp_ratio(amt); if(1) for( int i=0; i<inumframes; i++ ) { float s1 = shaper(inpbuf[i]*pad,amt); output1(dspbuf,i, s1 ); } }
static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch) { QString uc = QString().append(ch); Shaper shaper(face, script, uc); uc = uc.normalized(QString::NormalizationForm_D); Shaper decomposed(face, script, uc); if( shaper.shaper_item.num_glyphs != decomposed.shaper_item.num_glyphs ) goto error; for (unsigned int i = 0; i < shaper.shaper_item.num_glyphs; ++i) { if ((shaper.shaper_item.glyphs[i]&0xffffff) != (decomposed.shaper_item.glyphs[i]&0xffffff)) goto error; } return true; error: QString str = ""; int i = 0; while (i < uc.length()) { str += QString("%1 ").arg(uc[i].unicode(), 4, 16); ++i; } qDebug("%s: decomposedShaping of char %4x failed\n decomposedString: %s\n nglyphs=%d, decomposed nglyphs %d", face->family_name, ch.unicode(), str.toLatin1().data(), shaper.shaper_item.num_glyphs, decomposed.shaper_item.num_glyphs); str = ""; i = 0; while (i < shaper.shaper_item.num_glyphs) { str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16); ++i; } qDebug(" composed glyph result = %s", str.toLatin1().constData()); str = ""; i = 0; while (i < decomposed.shaper_item.num_glyphs) { str += QString("%1 ").arg(decomposed.shaper_item.glyphs[i], 4, 16); ++i; } qDebug(" decomposed glyph result = %s", str.toLatin1().constData()); return false; }
TEST_F(CachingWordShaperTest, GlyphBoundsWithSpaces) { CachingWordShaper shaper(cache.get()); TextRun periods(reinterpret_cast<const LChar*>(".........."), 10); FloatRect periodsGlyphBounds; float periodsWidth = shaper.width(&font, periods, nullptr, &periodsGlyphBounds); TextRun periodsAndSpaces(reinterpret_cast<const LChar*>(". . . . . . . . . ."), 19); FloatRect periodsAndSpacesGlyphBounds; float periodsAndSpacesWidth = shaper.width(&font, periodsAndSpaces, nullptr, &periodsAndSpacesGlyphBounds); // The total width of periods and spaces should be longer than the width of periods alone. ASSERT_GT(periodsAndSpacesWidth, periodsWidth); // The glyph bounds of periods and spaces should be longer than the glyph bounds of periods alone. ASSERT_GT(periodsAndSpacesGlyphBounds.width(), periodsGlyphBounds.width()); }
void InlineTextBox::characterWidths(Vector<float>& widths) const { FontCachePurgePreventer fontCachePurgePreventer; const ComputedStyle& styleToUse = lineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); TextRun textRun = constructTextRun(styleToUse, font); SimpleShaper shaper(&font, textRun); float lastWidth = 0; widths.resize(m_len); for (unsigned i = 0; i < m_len; i++) { shaper.advance(i + 1); widths[i] = shaper.runWidthSoFar() - lastWidth; lastWidth = shaper.runWidthSoFar(); } }
// Tests that filling a glyph buffer for a specific range returns the same // results when shaping word by word as when shaping the full run in one go. TEST_F(CachingWordShaperTest, CommonAccentLeftToRightFillGlyphBuffer) { // "/. ." with an accent mark over the first dot. const UChar str[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0}; TextRun textRun(str, 5); CachingWordShaper shaper(cache.get()); GlyphBuffer glyphBuffer; shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 0, 3); std::unique_ptr<ShapeCache> referenceCache = wrapUnique(new ShapeCache()); CachingWordShaper referenceShaper(referenceCache.get()); GlyphBuffer referenceGlyphBuffer; font.setCanShapeWordByWordForTesting(false); referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &referenceGlyphBuffer, 0, 3); ASSERT_EQ(referenceGlyphBuffer.glyphAt(0), glyphBuffer.glyphAt(0)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(1), glyphBuffer.glyphAt(1)); ASSERT_EQ(referenceGlyphBuffer.glyphAt(2), glyphBuffer.glyphAt(2)); }