示例#1
0
int Font::offsetForPositionForComplexText(const TextRun& run, float x,
                                          bool includePartialGlyphs) const
{
    // (Mac code ignores includePartialGlyphs, and they don't know what it's
    // supposed to do, so we just ignore it as well.)
    TextRunWalker walker(run, 0, 0, this);
    walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing());

    // If this is RTL text, the first glyph from the left is actually the last
    // code point. So we need to know how many code points there are total in
    // order to subtract. This is different from the length of the TextRun
    // because UTF-16 surrogate pairs are a single code point, but 32-bits long.
    // In LTR we leave this as 0 so that we get the correct value for
    // |basePosition|, below.
    unsigned totalCodePoints = 0;
    if (walker.rtl()) {
        ssize_t offset = 0;
        while (offset < run.length()) {
            utf16_to_code_point(run.characters(), run.length(), &offset);
            totalCodePoints++;
        }
    }

    unsigned basePosition = totalCodePoints;

    // For RTL:
    //   code-point order:  abcd efg hijkl
    //   on screen:         lkjih gfe dcba
    //                                ^   ^
    //                                |   |
    //                  basePosition--|   |
    //                 totalCodePoints----|
    // Since basePosition is currently the total number of code-points, the
    // first thing we do is decrement it so that it's pointing to the start of
    // the current script-run.
    //
    // For LTR, basePosition is zero so it already points to the start of the
    // first script run.
    while (walker.nextScriptRun()) {
        if (walker.rtl())
            basePosition -= walker.numCodePoints();

        if (x >= 0 && x < static_cast<int>(walker.width())) {
            // The x value in question is within this script run. We consider
            // each glyph in presentation order and stop when we find the one
            // covering this position.
            const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x);

            // Now that we have a glyph index, we have to turn that into a
            // code-point index. Because of ligatures, several code-points may
            // have gone into a single glyph. We iterate over the clusters log
            // and find the first code-point which contributed to the glyph.

            // Some shapers (i.e. Khmer) will produce cluster logs which report
            // that /no/ code points contributed to certain glyphs. Because of
            // this, we take any code point which contributed to the glyph in
            // question, or any subsequent glyph. If we run off the end, then
            // we take the last code point.
            const unsigned short* log = walker.logClusters();
            for (unsigned j = 0; j < walker.numCodePoints(); ++j) {
                if (log[j] >= glyphIndex)
                    return basePosition + j;
            }

            return basePosition + walker.numCodePoints() - 1;
        }

        x -= walker.width();

        if (!walker.rtl())
            basePosition += walker.numCodePoints();
    }

    return basePosition;
}
示例#2
0
    void walk(const TextRun& run, bool isVerticalText, const String& language, int from, int to)
    {
        // Should hold true for SVG text, otherwhise sth. is wrong
        ASSERT(to - from == run.length());

        Vector<SVGGlyphIdentifier::ArabicForm> chars(charactersWithArabicForm(String(run.data(from), run.length()), run.rtl()));

        SVGGlyphIdentifier identifier;
        bool foundGlyph = false;
        int characterLookupRange;
        int endOfScanRange = to + m_walkerData.extraCharsAvailable;

        bool haveAltGlyph = false;
        SVGGlyphIdentifier altGlyphIdentifier;
        if (RenderObject* renderObject = run.referencingRenderObject()) {
            if (renderObject->element() && renderObject->element()->hasTagName(SVGNames::altGlyphTag)) {
                SVGGlyphElement* glyphElement = static_cast<SVGAltGlyphElement*>(renderObject->element())->glyphElement();
                if (glyphElement) {
                    haveAltGlyph = true;
                    altGlyphIdentifier = glyphElement->buildGlyphIdentifier();
                    altGlyphIdentifier.isValid = true;
                    altGlyphIdentifier.nameLength = to - from;
                }
            }
        }

        for (int i = from; i < to; ++i) {
            // If characterLookupRange is > 0, then the font defined ligatures (length of unicode property value > 1).
            // We have to check wheter the current character & the next character define a ligature. This needs to be
            // extended to the n-th next character (where n is 'characterLookupRange'), to check for any possible ligature.
            characterLookupRange = endOfScanRange - i;

            String lookupString(run.data(i), characterLookupRange);
            Vector<SVGGlyphIdentifier> glyphs;
            if (haveAltGlyph)
                glyphs.append(altGlyphIdentifier);
            else
                m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs);

            Vector<SVGGlyphIdentifier>::iterator it = glyphs.begin();
            Vector<SVGGlyphIdentifier>::iterator end = glyphs.end();
            
            for (; it != end; ++it) {
                identifier = *it;
                if (identifier.isValid && isCompatibleGlyph(identifier, isVerticalText, language, chars, i, i + identifier.nameLength)) {
                    ASSERT(characterLookupRange > 0);
                    i += identifier.nameLength - 1;
                    m_walkerData.charsConsumed += identifier.nameLength;
                    m_walkerData.glyphName = identifier.glyphName;

                    foundGlyph = true;
                    SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
                    break;
                }
            }

            if (!foundGlyph) {
                ++m_walkerData.charsConsumed;
                if (SVGMissingGlyphElement* element = m_fontElement->firstMissingGlyphElement()) {
                    // <missing-glyph> element support
                    identifier = SVGGlyphElement::buildGenericGlyphIdentifier(element);
                    SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
                    identifier.isValid = true;
                } else {
                    // Fallback to system font fallback
                    TextRun subRun(run);
                    subRun.setText(subRun.data(i), 1);

                    (*m_walkerMissingGlyphCallback)(subRun, m_walkerData);
                    continue;
                }
            }

            if (!(*m_walkerCallback)(identifier, m_walkerData))
                break;

            foundGlyph = false;
        }
    }
static const QString qstring(const TextRun& run)
{
    // We don't detach
    return QString::fromRawData(reinterpret_cast<const QChar*>(run.characters()), run.length());
}
void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
{
    if (to < 0)
        to = run.length();

    QPainter *p = ctx->platformContext();

    if (ctx->textDrawingMode() & cTextFill) {
        if (ctx->fillGradient()) {
            QBrush brush(*ctx->fillGradient()->platformGradient());
            brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
            p->setPen(QPen(brush, 0));
        } else if (ctx->fillPattern()) {
            TransformationMatrix affine;
            p->setPen(QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0));
        } else
            p->setPen(QColor(ctx->fillColor()));
    }

    if (ctx->textDrawingMode() & cTextStroke) {
        if (ctx->strokeGradient()) {
            QBrush brush(*ctx->strokeGradient()->platformGradient());
            brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
            p->setPen(QPen(brush, ctx->strokeThickness()));
        } else if (ctx->strokePattern()) {
            TransformationMatrix affine;
            p->setPen(QPen(QBrush(ctx->strokePattern()->createPlatformPattern(affine)), ctx->strokeThickness()));
        } else
            p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness()));
    }

    const QString string = fixSpacing(qstring(run));

    // text shadow
    IntSize shadowSize;
    int shadowBlur;
    Color shadowColor;
    bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor);

    if (from > 0 || to < run.length()) {
        QTextLayout layout(string, font());
        QTextLine line = setupLayout(&layout, run);
        float x1 = line.cursorToX(from);
        float x2 = line.cursorToX(to);
        if (x2 < x1)
            qSwap(x1, x2);

        QFontMetrics fm(font());
        int ascent = fm.ascent();
        QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());

        if (hasShadow) {
            // TODO: when blur support is added, the clip will need to account
            // for the blur radius
            qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
            if (shadowSize.width() > 0)
                dx2 = shadowSize.width();
            else
                dx1 = -shadowSize.width();
            if (shadowSize.height() > 0)
                dy2 = shadowSize.height();
            else
                dy1 = -shadowSize.height();
            // expand the clip rect to include the text shadow as well
            clip.adjust(dx1, dx2, dy1, dy2);
        }
        p->save();
        p->setClipRect(clip.toRect());
        QPointF pt(point.x(), point.y() - ascent);
        if (hasShadow) {
            p->save();
            p->setPen(QColor(shadowColor));
            p->translate(shadowSize.width(), shadowSize.height());
            line.draw(p, pt);
            p->restore();
        }
        line.draw(p, pt);
        p->restore();
        return;
    }

    p->setFont(font());

    QPointF pt(point.x(), point.y());
    int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
    if (hasShadow) {
        // TODO: text shadow blur support
        p->save();
        p->setPen(QColor(shadowColor));
        p->translate(shadowSize.width(), shadowSize.height());
        p->drawText(pt, string, flags, run.padding());
        p->restore();
    }
    if (ctx->textDrawingMode() & cTextStroke) {
        QPainterPath path;
        path.addText(pt, font(), string);
        p->strokePath(path, p->pen());
    }
    if (ctx->textDrawingMode() & cTextFill)
        p->drawText(pt, string, flags, run.padding());
}
float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length)
#endif
{
    if (paintingDisabled())
#if !PLATFORM(IOS)
        return;
#else
        return 0;
#endif

    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
#if !PLATFORM(IOS)
    bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
#else
    bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride()));
#endif
    bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));

    // FIXME: This ownership should be reversed. We should pass BidiRunList
    // to BidiResolver in createBidiRunsForLine.
    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
#if !PLATFORM(IOS)
    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
#else
    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length));
#endif    
    if (!bidiRuns.runCount())
#if !PLATFORM(IOS)
        return;
#else
        return 0;
#endif

    FloatPoint currPoint = point;
    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
    while (bidiRun) {
        TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
        bool isRTL = bidiRun->level() % 2;
        subrun.setDirection(isRTL ? RTL : LTR);
        subrun.setDirectionalOverride(bidiRun->dirOverride(false));

#if !PLATFORM(IOS)
        font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);

        bidiRun = bidiRun->next();
        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
        if (bidiRun)
            currPoint.move(font.width(subrun), 0);
#else
        float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
        currPoint.move(width, 0);

        bidiRun = bidiRun->next();
#endif
    }

#if PLATFORM(IOS)
    if (status)
        *status = bidiResolver.status();
#endif
    bidiRuns.deleteRuns();

#if PLATFORM(IOS)
    return currPoint.x() - static_cast<float>(point.x());
#endif
}
示例#6
0
Font::CodePath Font::codePath(const TextRun& run) const
{
    if (s_codePath != Auto)
        return s_codePath;

#if PLATFORM(QT)
    if (run.padding() || run.rtl() || isSmallCaps() || wordSpacing() || letterSpacing())
        return Complex;
#endif

    CodePath result = Simple;

    // Start from 0 since drawing and highlighting also measure the characters before run->from
    for (int i = 0; i < run.length(); i++) {
        const UChar c = run[i];
        if (c < 0x300) // U+0300 through U+036F Combining diacritical marks
            continue;
        if (c <= 0x36F)
            return Complex;

        if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
            continue;
        if (c <= 0x05CF)
            return Complex;

        if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
            continue;
        if (c <= 0x1059)
            return Complex;

        if (c < 0x1100) // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose; Modern Korean will be precomposed as a result of step A)
            continue;
        if (c <= 0x11FF)
            return Complex;

        if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian
            continue;
        if (c <= 0x18AF)
            return Complex;

        if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0)
            continue;
        if (c <= 0x194F)
            return Complex;

        if (c < 0x1E00) // U+1E00 through U+2000 characters with diacritics and stacked diacritics
            continue;
        if (c <= 0x2000) {
            result = SimpleWithGlyphOverflow;
            continue;
        }

        if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols
            continue;
        if (c <= 0x20FF)
            return Complex;

        if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks
            continue;
        if (c <= 0xFE2F)
            return Complex;
    }

    if (typesettingFeatures())
        return Complex;

    return result;
}
static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
{
    if (to < 0)
        to = run.length();

    QPainter *p = ctx->platformContext();

    QPen textFillPen;
    if (ctx->textDrawingMode() & cTextFill) {
        if (ctx->fillGradient()) {
            QBrush brush(*ctx->fillGradient()->platformGradient());
            brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
            textFillPen = QPen(brush, 0);
        } else if (ctx->fillPattern()) {
            AffineTransform affine;
            textFillPen = QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
        } else
            textFillPen = QPen(QColor(ctx->fillColor()));
    }

    QPen textStrokePen;
    if (ctx->textDrawingMode() & cTextStroke) {
        if (ctx->strokeGradient()) {
            QBrush brush(*ctx->strokeGradient()->platformGradient());
            brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
            textStrokePen = QPen(brush, ctx->strokeThickness());
        } else if (ctx->strokePattern()) {
            AffineTransform affine;
            QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
            textStrokePen = QPen(brush, ctx->strokeThickness());
        } else
            textStrokePen = QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
    }

    String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    QString string = fromRawDataWithoutRef(sanitized);
    QPointF pt(point.x(), point.y());

    // text shadow
    IntSize shadowSize;
    int shadowBlur;
    Color shadowColor;
    bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor);

    if (from > 0 || to < run.length()) {
        if (isComplexText) {
            QTextLayout layout(string, font);
            QTextLine line = setupLayout(&layout, run);
            float x1 = line.cursorToX(from);
            float x2 = line.cursorToX(to);
            if (x2 < x1)
                qSwap(x1, x2);

            QFontMetrics fm(font);
            int ascent = fm.ascent();
            QRectF clip(fm.boundingRect(string));

            if (hasShadow) {
                // TODO: when blur support is added, the clip will need to account
                // for the blur radius
                qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
                if (shadowSize.width() > 0)
                    dx2 = shadowSize.width();
                else
                    dx1 = -shadowSize.width();
                if (shadowSize.height() > 0)
                    dy2 = shadowSize.height();
                else
                    dy1 = -shadowSize.height();
                // expand the clip rect to include the text shadow as well
                clip.adjust(dx1, dx2, dy1, dy2);
            }
            p->save();
            //p->setClipRect(clip.toRect(), Qt::IntersectClip);
            pt.setY(pt.y() - ascent);
            if (hasShadow) {
                p->save();
                p->setPen(QColor(shadowColor));
                p->translate(shadowSize.width(), shadowSize.height());
                line.draw(p, pt);
                p->restore();
            }
            p->setPen(textFillPen);
            line.draw(p, pt);
            p->restore();
            return;
        }
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
        int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
        pt.setX(pt.x() + skipWidth);
        string = fromRawDataWithoutRef(sanitized, from, to - from);
#endif
    }

    p->setFont(font);

    int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
    // See QWebPagePrivate::QWebPagePrivate() where the default path is set to Complex for Qt 4.6 and earlier.
    if (!isComplexText)
        flags |= Qt::TextBypassShaping;
#endif

    QFontMetrics fm(font);
    QRectF clip(pt.x(), pt.y()-fm.ascent(), fm.width(string, -1, flags), fm.height());

    if (hasShadow) {
        // TODO: text shadow blur support
        p->save();
        p->translate(shadowSize.width(), shadowSize.height());
        //p->setClipRect(clip, Qt::IntersectClip);
        p->setPen(QColor(shadowColor));
        p->drawText(pt, string, flags, run.padding());
        p->restore();
    }
    if (ctx->textDrawingMode() & cTextStroke) {
        QPainterPath path;
        path.addText(pt, font, string);
        p->save();
        //p->setClipRect(clip, Qt::IntersectClip);
        p->setPen(textStrokePen);
        p->strokePath(path, p->pen());
        p->restore();
    }
    if (ctx->textDrawingMode() & cTextFill) {
        p->save();
        //p->setClipRect(clip, Qt::IntersectClip);
        p->setPen(textFillPen);
        p->drawText(pt, string, flags, run.padding());
        p->restore();
    }
}
示例#8
0
static int generateComponents(TextRunComponents* components, const Font &font, const TextRun &run)
{
    int letterSpacing = font.letterSpacing();
    int wordSpacing = font.wordSpacing();
    int padding = run.padding();
    int numSpaces = 0;
    if (padding) {
        for (int i = 0; i < run.length(); i++)
            if (Font::treatAsSpace(run[i]))
                ++numSpaces;
    }

    int offset = 0;
    if (letterSpacing) {
        // need to draw every letter on it's own
        int start = 0;
        if (Font::treatAsSpace(run[0])) {
            int add = 0;
            if (numSpaces) {
                add = padding/numSpaces;
                padding -= add;
                --numSpaces;
            }
            components->append(TextRunComponent(1, font, offset));
            offset += add + letterSpacing + components->last().m_width;
            start = 1;
        }
        for (int i = 1; i < run.length(); ++i) {
            uint ch = run[i];
            if (isHighSurrogate(ch) && isLowSurrogate(run[i-1]))
                ch = surrogateToUcs4(ch, run[i-1]);
            if (isLowSurrogate(ch) || category(ch) == Mark_NonSpacing)
                continue;
            if (Font::treatAsSpace(run[i])) {
                int add = 0;
                if (i - start > 0) {
                    components->append(TextRunComponent(run.characters() + start, i - start,
                                                        run, font, offset));
                    offset += components->last().m_width + letterSpacing;
                }
                if (numSpaces) {
                    add = padding/numSpaces;
                    padding -= add;
                    --numSpaces;
                }
                components->append(TextRunComponent(1, font, offset));
                offset += wordSpacing + add + components->last().m_width + letterSpacing;
                start = i + 1;
                continue;
            }
            if (i - start > 0) {
                components->append(TextRunComponent(run.characters() + start, i - start,
                                                    run,
                                                    font, offset));
                offset += components->last().m_width + letterSpacing;
            }
            start = i;
        }
        if (run.length() - start > 0) {
            components->append(TextRunComponent(run.characters() + start, run.length() - start,
                                                run,
                                                font, offset));
            offset += components->last().m_width;
        }
        offset += letterSpacing;
    } else {
        int start = 0;
        for (int i = 0; i < run.length(); ++i) {
            if (Font::treatAsSpace(run[i])) {
                if (i - start > 0) {
                    components->append(TextRunComponent(run.characters() + start, i - start,
                                                        run,
                                                        font, offset));
                    offset += components->last().m_width;
                }
                int add = 0;
                if (numSpaces) {
                    add = padding/numSpaces;
                    padding -= add;
                    --numSpaces;
                }
                components->append(TextRunComponent(1, font, offset));
                offset += add + components->last().m_width;
                if (i)
                    offset += wordSpacing;
                start = i + 1;
            }
        }
        if (run.length() - start > 0) {
            components->append(TextRunComponent(run.characters() + start, run.length() - start,
                                                run,
                                                font, offset));
            offset += components->last().m_width;
        }
    }
    return offset;
}
示例#9
0
文件: Font.cpp 项目: hinogi/phantomjs
Font::CodePath Font::codePath(const TextRun& run) const
{
    if (s_codePath != Auto)
        return s_codePath;

#if PLATFORM(QT) && !HAVE(QRAWFONT)
    if (run.expansion() || run.rtl() || isSmallCaps() || wordSpacing() || letterSpacing())
        return Complex;
#endif

    CodePath result = Simple;

    // Start from 0 since drawing and highlighting also measure the characters before run->from
    // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we
    // can't simply use UnicodeCharacter Property/class because some characters
    // are not 'combining', but still need to go to the complex path.
    // Alternatively, we may as well consider binary search over a sorted
    // list of ranges.
    for (int i = 0; i < run.length(); i++) {
        const UChar c = run[i];
        if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
            continue;
        if (c <= 0x2E9)
            return Complex;

        if (c < 0x300) // U+0300 through U+036F Combining diacritical marks
            continue;
        if (c <= 0x36F)
            return Complex;

        if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
            continue;
        if (c <= 0x05CF)
            return Complex;

        // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic,
        // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
        // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
        if (c < 0x0600)
            continue;
        if (c <= 0x109F)
            return Complex;

        // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left here if you precompose;
        // Modern Korean will be precomposed as a result of step A)
        if (c < 0x1100)
            continue;
        if (c <= 0x11FF)
            return Complex;

        if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks
            continue;
        if (c <= 0x135F)
            return Complex;

        if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
            continue;
        if (c <= 0x18AF)
            return Complex;

        if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0)
            continue;
        if (c <= 0x194F)
            return Complex;

        if (c < 0x1980) // U+1980 through U+19DF New Tai Lue
            continue;
        if (c <= 0x19DF)
            return Complex;

        if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
            continue;
        if (c <= 0x1CFF)
            return Complex;

        if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement
            continue;
        if (c <= 0x1DFF)
            return Complex;

        // U+1E00 through U+2000 characters with diacritics and stacked diacritics
        if (c <= 0x2000) {
            result = SimpleWithGlyphOverflow;
            continue;
        }

        if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols
            continue;
        if (c <= 0x20FF)
            return Complex;

        if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic
            continue;
        if (c <= 0x2CF1)
            return Complex;

        if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks
            continue;
        if (c <= 0x302F)
            return Complex;

        if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic
            continue;
        if (c <= 0xA67D)
            return Complex;

        if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum
            continue;
        if (c <= 0xA6F1)
            return Complex;

        // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
        // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek,
        if (c < 0xA800)
            continue;
        if (c <= 0xABFF)
            return Complex;

        if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B
            continue;
        if (c <= 0xD7FF)
            return Complex;

        if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks
            continue;
        if (c <= 0xFE2F)
            return Complex;

        // FIXME: Make this loop UTF-16-aware and check for Brahmi (U+11000 block)
        // Kaithi (U+11080 block) and other complex scripts in plane 1 or higher.
    }

    if (typesettingFeatures())
        return Complex;

    return result;
}
示例#10
0
float Font::floatWidthForComplexText(const TextRun& run, const TextStyle&) const
{
    // FIXME: style
    QFontMetricsF metrics(primaryFont()->m_font.font());
    return metrics.width(QString::fromRawData(reinterpret_cast<const QChar*>(run.characters() + run.from()), run.length()));
}
示例#11
0
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
{
    cairo_t* cr = context->platformContext();
    cairo_save(cr);
    cairo_translate(cr, point.x(), point.y());

    PangoLayout* layout = pango_cairo_create_layout(cr);
    setPangoAttributes(this, run, layout);

    gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length());
    pango_layout_set_text(layout, utf8, -1);

    // Our layouts are single line
    PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0);

    GdkRegion* partialRegion = NULL;
    if (to - from != run.length()) {
        // Clip the region of the run to be rendered
        char* start = g_utf8_offset_to_pointer(utf8, from);
        char* end = g_utf8_offset_to_pointer(start, to - from);
        int ranges[] = {start - utf8, end - utf8};
        partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1);
        gdk_region_shrink(partialRegion, 0, -pixelSize());
    }

    Color fillColor = context->fillColor();
    float red, green, blue, alpha;

    // Text shadow, inspired by FontMac
    IntSize shadowSize;
    int shadowBlur = 0;
    Color shadowColor;
    bool hasShadow = context->textDrawingMode() == cTextFill &&
        context->getShadow(shadowSize, shadowBlur, shadowColor);

    // TODO: Blur support
    if (hasShadow) {
        // Disable graphics context shadows (not yet implemented) and paint them manually
        context->clearShadow();
        Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255);
        cairo_save(cr);

        shadowFillColor.getRGBA(red, green, blue, alpha);
        cairo_set_source_rgba(cr, red, green, blue, alpha);

        cairo_translate(cr, shadowSize.width(), shadowSize.height());

        if (partialRegion) {
            gdk_cairo_region(cr, partialRegion);
            cairo_clip(cr);
        }

        pango_cairo_show_layout_line(cr, layoutLine);

        cairo_restore(cr);
    }

    fillColor.getRGBA(red, green, blue, alpha);
    cairo_set_source_rgba(cr, red, green, blue, alpha);

    if (partialRegion) {
        gdk_cairo_region(cr, partialRegion);
        cairo_clip(cr);
    }

    pango_cairo_show_layout_line(cr, layoutLine);

    if (context->textDrawingMode() & cTextStroke) {
        Color strokeColor = context->strokeColor();
        strokeColor.getRGBA(red, green, blue, alpha);
        cairo_set_source_rgba(cr, red, green, blue, alpha);
        pango_cairo_layout_line_path(cr, layoutLine);
        cairo_set_line_width(cr, context->strokeThickness());
        cairo_stroke(cr);
    }

    // Re-enable the platform shadow we disabled earlier
    if (hasShadow)
        context->setShadow(shadowSize, shadowBlur, shadowColor);

    // Pango sometimes leaves behind paths we don't want
    cairo_new_path(cr);

    if (partialRegion)
        gdk_region_destroy(partialRegion);

    g_free(utf8);
    g_object_unref(layout);

    cairo_restore(cr);
}
示例#12
0
float Font::floatWidthForComplexText(const TextRun& run, const TextStyle& style) const
{
    UniscribeController controller(this, run, style);
    controller.advance(run.length());
    return controller.runWidthSoFar();
}
示例#13
0
// SDL ttf implementation may be lighter ???
void Font::drawSimpleText(BIGraphicsContext* context, const TextRun& run, const TextStyle& style, const FloatPoint& point) const
{
    WebCore::Color  text_color = context->strokeColor();
    BINativeImage*  text_surface;
    IntRect         dst_rect;
    IntPoint        intPoint;
    static int oldx = 0;
    int         text_width, text_height;
    bool        init = false;
    int         numSpaces = 0;
    int         padPerSpace = 0;
    FloatPoint	wordPoint(point);

    // Process normal, bold, italic styles
    if (m_fontDescription.italic()) {
        if (m_fontDescription.bold()) {
            // Bold && italic
            d->m_ttfFont->style = FT_STYLE_BOLD | FT_STYLE_ITALIC;
            d->flushCache(d->m_ttfFont);
        } else {
            // Only italic
            d->m_ttfFont->style = FT_STYLE_ITALIC;
            d->flushCache(d->m_ttfFont);
        }
    } else if (m_fontDescription.bold()) {
        // Only bold
        d->m_ttfFont->style = FT_STYLE_BOLD;
        d->flushCache(d->m_ttfFont);
    } else {
        d->m_ttfFont->style = FT_STYLE_NORMAL;
        d->flushCache(d->m_ttfFont);
    }

    // Draw font
    int wordSize = run.length() - run.from();
    UChar word[wordSize];
    copyTextRunTo(run, word);

    for (int i = 0; i <= wordSize; i++) {
        if (Font::treatAsSpace(word[i]))
            numSpaces++;;
    }

    if (numSpaces == 0)
        padPerSpace = 0;
    else
        padPerSpace = static_cast<int> (ceil(style.padding() / numSpaces));

    UChar text[wordSize + 1];
    int j = 0;
    for (int i = 0; i <= wordSize; i++) {
        if (Font::treatAsSpace(word[i]) || i == wordSize) {
            text[j] = ' ';
            text[++j] = '\0';

			d->sizeUnicode(d->m_ttfFont, text, &text_width, &text_height);
			text_surface = d->renderUnicodeBlended(d->m_ttfFont, text, text_color, context->alphaLayer());
			if (text_surface) {
    			intPoint.setX(static_cast<uint16_t> (wordPoint.x()));
    			intPoint.setY(static_cast<uint16_t> (wordPoint.y()) - ascent());
                dst_rect.setX(0);
                dst_rect.setY(0);
                dst_rect.setWidth(text_surface->size().width());
                dst_rect.setHeight(text_surface->size().height());
                getBIGraphicsDevice()->copy(*(context->widget()), *text_surface, dst_rect, intPoint, context->alphaLayer());
                delete text_surface;
            }
            wordPoint = wordPoint + FloatSize(padPerSpace + text_width,0);
            j = 0;
        } else {
            text[j] = word[i];
            j++;
        }
    }
}
示例#14
0
文件: FontQt.cpp 项目: Fale/qtmoko
void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
{
    if (to < 0)
        to = run.length();

    QPainter *p = ctx->platformContext();
    Color color = ctx->fillColor();
    p->setPen(QColor(color));

    QString string = qstring(run);

    // text shadow
    IntSize shadowSize;
    int shadowBlur;
    Color shadowColor;
    bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor);

    if (from > 0 || to < run.length()) {
        QTextLayout layout(string, font());
        QTextLine line = setupLayout(&layout, run);
        float x1 = line.cursorToX(from);
        float x2 = line.cursorToX(to);
        if (x2 < x1)
            qSwap(x1, x2);

        QFontMetrics fm(font());
        int ascent = fm.ascent();
        QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());

        if (hasShadow) {
            // TODO: when blur support is added, the clip will need to account
            // for the blur radius
            qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
            if (shadowSize.width() > 0)
                dx2 = shadowSize.width();
            else
                dx1 = -shadowSize.width();
            if (shadowSize.height() > 0)
                dy2 = shadowSize.height();
            else
                dy1 = -shadowSize.height();
            // expand the clip rect to include the text shadow as well
            clip.adjust(dx1, dx2, dy1, dy2);
        }
        p->save();
        p->setClipRect(clip.toRect());
        QPointF pt(point.x(), point.y() - ascent);
        if (hasShadow) {
            p->save();
            p->setPen(QColor(shadowColor));
            p->translate(shadowSize.width(), shadowSize.height());
            line.draw(p, pt);
            p->restore();
        }
        line.draw(p, pt);
        p->restore();
        return;
    }

    p->setFont(font());

    QPointF pt(point.x(), point.y());
    int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
    if (hasShadow) {
        // TODO: text shadow blur support
        p->save();
        p->setPen(QColor(shadowColor));
        p->translate(shadowSize.width(), shadowSize.height());
        p->drawText(pt, string, flags, run.padding());
        p->restore();
    }
    p->drawText(pt, string, flags, run.padding());
}
示例#15
0
static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
{
    if (to < 0)
        to = run.length();

    QPainter *p = ctx->platformContext();

    QPen textFillPen;
    if (ctx->textDrawingMode() & TextModeFill) {
        if (ctx->fillGradient()) {
            QBrush brush(*ctx->fillGradient()->platformGradient());
            brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
            textFillPen = QPen(brush, 0);
        } else if (ctx->fillPattern()) {
            AffineTransform affine;
            textFillPen = QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
        } else
            textFillPen = QPen(QColor(ctx->fillColor()));
    }

    QPen textStrokePen;
    if (ctx->textDrawingMode() & TextModeStroke) {
        if (ctx->strokeGradient()) {
            QBrush brush(*ctx->strokeGradient()->platformGradient());
            brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
            textStrokePen = QPen(brush, ctx->strokeThickness());
        } else if (ctx->strokePattern()) {
            AffineTransform affine;
            QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
            textStrokePen = QPen(brush, ctx->strokeThickness());
        } else
            textStrokePen = QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
    }

    String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    QString string = fromRawDataWithoutRef(sanitized);
    QPointF pt(point.x(), point.y());

    if (from > 0 || to < run.length()) {
        if (isComplexText) {
            QTextLayout layout(string, font);
            QTextLine line = setupLayout(&layout, run);
            float x1 = line.cursorToX(from);
            float x2 = line.cursorToX(to);
            if (x2 < x1)
                qSwap(x1, x2);

            QFontMetrics fm(font);
            int ascent = fm.ascent();
            QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
            QRectF clip = boundingRect;

            ContextShadow* ctxShadow = ctx->contextShadow();

            if (ctxShadow->m_type != ContextShadow::NoShadow) {
                qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
                if (ctxShadow->offset().x() > 0)
                    dx2 = ctxShadow->offset().x();
                else
                    dx1 = -ctxShadow->offset().x();
                if (ctxShadow->offset().y() > 0)
                    dy2 = ctxShadow->offset().y();
                else
                    dy1 = -ctxShadow->offset().y();
                // expand the clip rect to include the text shadow as well
                clip.adjust(dx1, dx2, dy1, dy2);
                clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance);
            }
            p->save();
            p->setClipRect(clip.toRect(), Qt::IntersectClip);
            pt.setY(pt.y() - ascent);

            if (ctxShadow->m_type != ContextShadow::NoShadow) {
                ContextShadow* ctxShadow = ctx->contextShadow();
                if (!ctxShadow->mustUseContextShadow(p)) {
                    p->save();
                    p->setPen(ctxShadow->m_color);
                    p->translate(ctxShadow->offset());
                    line.draw(p, pt);
                    p->restore();
                } else {
                    QPainter* shadowPainter = ctxShadow->beginShadowLayer(p, boundingRect);
                    if (shadowPainter) {
                        // Since it will be blurred anyway, we don't care about render hints.
                        shadowPainter->setFont(p->font());
                        shadowPainter->setPen(ctxShadow->m_color);
                        line.draw(shadowPainter, pt);
                        ctxShadow->endShadowLayer(p);
                    }
                }
            }
            p->setPen(textFillPen);
            line.draw(p, pt);
            p->restore();
            return;
        }
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
        int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
        pt.setX(pt.x() + skipWidth);
        string = fromRawDataWithoutRef(sanitized, from, to - from);
#endif
    }

    p->setFont(font);

    int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
    // See QWebPagePrivate::QWebPagePrivate() where the default path is set to Complex for Qt 4.6 and earlier.
    if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
        flags |= Qt::TextBypassShaping;
#endif

    QPainterPath textStrokePath;
    if (ctx->textDrawingMode() & TextModeStroke)
        textStrokePath.addText(pt, font, string);

    ContextShadow* ctxShadow = ctx->contextShadow();
    if (ctxShadow->m_type != ContextShadow::NoShadow) {
        if (ctx->textDrawingMode() & TextModeFill) {
            if (ctxShadow->m_type != ContextShadow::BlurShadow) {
                p->save();
                p->setPen(ctxShadow->m_color);
                p->translate(ctxShadow->offset());
                p->drawText(pt, string, flags, run.padding());
                p->restore();
            } else {
                QFontMetrics fm(font);
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
                QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
#else
                QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string), fm.height());
#endif
                QPainter* shadowPainter = ctxShadow->beginShadowLayer(p, boundingRect);
                if (shadowPainter) {
                    // Since it will be blurred anyway, we don't care about render hints.
                    shadowPainter->setFont(p->font());
                    shadowPainter->setPen(ctxShadow->m_color);
                    shadowPainter->drawText(pt, string, flags, run.padding());
                    ctxShadow->endShadowLayer(p);
                }
            }
        } else if (ctx->textDrawingMode() & TextModeStroke) {
            if (ctxShadow->m_type != ContextShadow::BlurShadow) {
                p->translate(ctxShadow->offset());
                p->strokePath(textStrokePath, QPen(ctxShadow->m_color));
                p->translate(-ctxShadow->offset());
            } else {
                QFontMetrics fm(font);
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
                QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
#else
                QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string), fm.height());
#endif
                QPainter* shadowPainter = ctxShadow->beginShadowLayer(p, boundingRect);
                if (shadowPainter) {
                    // Since it will be blurred anyway, we don't care about render hints.
                    shadowPainter->setFont(p->font());
                    shadowPainter->strokePath(textStrokePath, QPen(ctxShadow->m_color));
                    ctxShadow->endShadowLayer(p);
                }
            }
        }
    }

    if (ctx->textDrawingMode() & TextModeStroke)
        p->strokePath(textStrokePath, textStrokePen);

    if (ctx->textDrawingMode() & TextModeFill) {
        QPen previousPen = p->pen();
        p->setPen(textFillPen);
        p->drawText(pt, string, flags, run.padding());
        p->setPen(previousPen);
    }
}