예제 #1
0
QList<TextShaper::TextRun> TextShaper::itemizeScripts(const QList<TextRun> &runs)
{
	QList<TextRun> newRuns;
	ScriptRun scriptrun((const UChar*) m_text.utf16(), m_text.length());

	foreach (TextRun run, runs)
	{
		int start = run.start;
		QList<TextRun> subRuns;

		while (scriptrun.next())
		{
			if (scriptrun.getScriptStart() <= start && scriptrun.getScriptEnd() > start)
				break;
		}

		while (start < run.start + run.len)
		{
			int end = qMin(scriptrun.getScriptEnd(), run.start + run.len);
			UScriptCode script = scriptrun.getScriptCode();
			if (run.dir == UBIDI_RTL)
				subRuns.prepend(TextRun(start, end - start, run.dir, script));
			else
				subRuns.append(TextRun(start, end - start, run.dir, script));

			start = end;
			scriptrun.next();
		}

		scriptrun.reset();
		newRuns.append(subRuns);
	}
예제 #2
0
static float textWidth(const RenderText& text, unsigned from, unsigned length, float xPosition, const RenderStyle& style)
{
    if (style.font().isFixedPitch() || (!from && length == text.textLength()))
        return text.width(from, length, style.font(), xPosition, nullptr, nullptr);
    // FIXME: Add templated UChar/LChar paths.
    TextRun run = text.is8Bit() ? TextRun(text.characters8() + from, length) : TextRun(text.characters16() + from, length);
    run.setCharactersLength(text.textLength() - from);
    ASSERT(run.charactersLength() >= run.length());

    run.setXPos(xPosition);
    return style.font().width(run);
}
예제 #3
0
QList<TextShaper::TextRun> TextShaper::itemizeBiDi()
{
	QList<TextRun> textRuns;
	UBiDi *obj = ubidi_open();
	UErrorCode err = U_ZERO_ERROR;

	UBiDiLevel parLevel = UBIDI_LTR;
	ParagraphStyle style = m_story.paragraphStyle(m_firstChar);
	if (style.direction() == ParagraphStyle::RTL)
		parLevel = UBIDI_RTL;

	ubidi_setPara(obj, (const UChar*) m_text.utf16(), m_text.length(), parLevel, NULL, &err);
	if (U_SUCCESS(err))
	{
		int32_t count = ubidi_countRuns(obj, &err);
		if (U_SUCCESS(err))
		{
			textRuns.reserve(count);
			for (int32_t i = 0; i < count; i++)
			{
				int32_t start, length;
				UBiDiDirection dir = ubidi_getVisualRun(obj, i, &start, &length);
				textRuns.append(TextRun(start, length, dir));
			}
		}
	}

	ubidi_close(obj);
	return textRuns;
}
예제 #4
0
void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = m_object->style(m_firstLine);
    if (style->font() != context->font())
        context->setFont(style->font());

    Color textColor = style->color();
    if (textColor != context->fillColor())
        context->setFillColor(textColor);
    bool setShadow = false;
    if (style->textShadow()) {
        context->setShadow(IntSize(style->textShadow()->x, style->textShadow()->y),
                           style->textShadow()->blur, style->textShadow()->color);
        setShadow = true;
    }

    const String& str = m_str;
    TextStyle textStyle(0, 0, 0, false, style->visuallyOrdered());
    context->drawText(TextRun(str.impl()), IntPoint(m_x + tx, m_y + ty + m_baseline), textStyle);

    if (setShadow)
        context->clearShadow();

    if (m_markupBox) {
        // Paint the markup box
        tx += m_x + m_width - m_markupBox->xPos();
        ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
        m_markupBox->paint(paintInfo, tx, ty);
    }
}
예제 #5
0
void PopupMenu::calculatePositionAndSize(const IntRect& r, FrameView* v)
{
    IntRect rScreenCoords(v->contentsToWindow(r.location()), r.size());
    rScreenCoords.setY(rScreenCoords.y() + rScreenCoords.height());
    
    m_itemHeight = rScreenCoords.height();

    int itemCount = client()->listSize();
    int naturalHeight = m_itemHeight * itemCount;

    int popupWidth = 0;
    for (int i = 0; i < itemCount; ++i) {
        String text = client()->itemText(i);
        if (text.isEmpty())
            continue;

        Font itemFont = client()->clientStyle()->font();
        if (client()->itemIsLabel(i)) {
            FontDescription d = itemFont.fontDescription();
            d.setWeight(d.bolderWeight());
            itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
            itemFont.update(m_popupClient->fontSelector());
        }

        popupWidth = max(popupWidth, itemFont.width(TextRun(text.characters(), text.length())));
    }
    
    rScreenCoords.setHeight(naturalHeight);
    rScreenCoords.setWidth(popupWidth + 10);

    m_windowRect = rScreenCoords;
}
예제 #6
0
void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font*)
{
    // See if we have a selection to paint at all.
    int sPos, ePos;
    selectionStartEnd(sPos, ePos);
    if (sPos >= ePos)
        return;

    Color textColor = style->color();
    Color c = object()->selectionBackgroundColor();
    if (!c.isValid() || c.alpha() == 0)
        return;

    // If the text color ends up being the same as the selection background, invert the selection
    // background.  This should basically never happen, since the selection has transparency.
    if (textColor == c)
        c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());

    context->save();
    updateGraphicsContext(context, c, c, 0);  // Don't draw text at all!
    int y = selectionTop();
    int h = selectionHeight();
    context->clip(IntRect(m_x + tx, y + ty, m_width, h));
    context->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
                            IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
    context->restore();
}
예제 #7
0
static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, FallThrough fallthrough)
{
    AvoidanceReasonFlags reasons = NoReason;
    // We assume that all lines have metrics based purely on the primary font.
    const auto& style = flow.style();
    auto& primaryFont = style.fontCascade().primaryFont();
    if (primaryFont.isLoading())
        SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsMissingPrimaryFont, fallthrough);
    if (primaryFont.isSVGFont())
        SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasSVGFont, fallthrough);

    for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
        if (textRenderer.isCombineText())
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsCombineText, fallthrough);
        if (textRenderer.isCounter())
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsRenderCounter, fallthrough);
        if (textRenderer.isQuote())
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsRenderQuote, fallthrough);
        if (textRenderer.isTextFragment())
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsTextFragment, fallthrough);
        if (textRenderer.isSVGInlineText())
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsSVGInlineText, fallthrough);
        if (style.fontCascade().codePath(TextRun(textRenderer.text())) != FontCascade::Simple)
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowFontIsNotSimple, fallthrough);

        auto textReasons = canUseForText(textRenderer, primaryFont, fallthrough);
        if (textReasons != NoReason)
            SET_REASON_AND_RETURN_IF_NEEDED(reasons, textReasons, fallthrough);
    }
    return reasons;
}
예제 #8
0
IntRect EllipsisBox::selectionRect(int tx, int ty)
{
    RenderStyle* style = m_renderer->style(m_firstLine);
    const Font& f = style->font();
    return enclosingIntRect(f.selectionRectForText(TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()),
                            IntPoint(m_x + tx, m_y + ty + root()->selectionTop()), root()->selectionHeight()));
}
예제 #9
0
static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
{
    ASSERT(style);

    bool directionalOverride = style->rtlOrdering() == VisualOrder;
    return TextRun(characters, length, 0, 0, expansion, direction, directionalOverride);
}
예제 #10
0
int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
{
    if (isLineBreak())
        return 0;

    RenderText* text = static_cast<RenderText*>(m_object);
    RenderStyle *style = text->style(m_firstLine);
    const Font* f = &style->font();
    return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
                                _x - m_x, includePartialGlyphs);
}
String FileChooser::basenameForWidth(const Font& font, int width) const 
{
    if (!m_filenames.size())
        return String();
    // FIXME: This could be a lot faster, but assuming the data will not
    // often be much longer than the provided width, this may be fast enough.
    String output = m_filenames[0].threadsafeCopy();
    while (font.width(TextRun(output.impl())) > width && output.length() > 4)
        output = output.replace(output.length() - 4, 4, String("..."));
    return output;
}
예제 #12
0
 Style(const RenderStyle& style)
     : font(style.font())
     , textAlign(style.textAlign())
     , collapseWhitespace(style.collapseWhiteSpace())
     , preserveNewline(style.preserveNewline())
     , wrapLines(style.autoWrap())
     , breakWordOnOverflow(style.overflowWrap() == BreakOverflowWrap && (wrapLines || preserveNewline))
     , spaceWidth(font.width(TextRun(&space, 1)))
     , tabWidth(collapseWhitespace ? 0 : style.tabSize())
 {
 }
예제 #13
0
void RenderCombineText::combineText()
{
    if (!m_needsFontUpdate)
        return;

    setIsCombined(false);
    setNeedsFontUpdate(false);

    // CSS3 spec says text-combine works only in vertical writing mode.
    if (style()->isHorizontalWritingMode())
        return;

    TextRun run = TextRun(String(text()));
    FontDescription description = originalFont().fontDescription();
    float emWidth = description.computedSize() * textCombineMargin;
    bool shouldUpdateFont = false;

    description.setOrientation(Horizontal); // We are going to draw combined text horizontally.
    setCombinedTextWidth(originalFont().width(run));
    setIsCombined(m_combinedTextWidth <= emWidth);

    if (m_isCombined)
        shouldUpdateFont = style()->setFontDescription(description); // Need to change font orientation to horizontal.
    else {
        // Need to try compressed glyphs.
        static const FontWidthVariant widthVariants[] = { HalfWidth, ThirdWidth, QuarterWidth };
        for (size_t i = 0 ; i < WTF_ARRAY_LENGTH(widthVariants) ; ++i) {
            description.setWidthVariant(widthVariants[i]);
            Font compressedFont = Font(description, style()->font().letterSpacing(), style()->font().wordSpacing());
            compressedFont.update(style()->font().fontSelector());
            float runWidth = compressedFont.width(run);
            if (runWidth <= emWidth) {
                setCombinedTextWidth(runWidth);
                setIsCombined(true);

                // Replace my font with the new one.
                shouldUpdateFont = style()->setFontDescription(description);
                break;
            }
        }
    }

    if (!m_isCombined)
        shouldUpdateFont = style()->setFontDescription(originalFont().fontDescription());

    if (shouldUpdateFont)
        style()->font().update(style()->font().fontSelector());

    if (m_isCombined) {
        DEFINE_STATIC_LOCAL(String, objectReplacementCharacterString, (&objectReplacementCharacter, 1));
        RenderText::setTextInternal(objectReplacementCharacterString.impl());
    }
}
TextFragmentIterator::Style::Style(const RenderStyle& style)
    : font(style.fontCascade())
    , textAlign(style.textAlign())
    , collapseWhitespace(style.collapseWhiteSpace())
    , preserveNewline(style.preserveNewline())
    , wrapLines(style.autoWrap())
    , breakWordOnOverflow(style.overflowWrap() == BreakOverflowWrap && (wrapLines || preserveNewline))
    , spaceWidth(font.width(TextRun(StringView(&space, 1))))
    , tabWidth(collapseWhitespace ? 0 : style.tabSize())
    , locale(style.locale())
{
}
예제 #15
0
String FileChooser::basenameForWidth(const Font& font, int width) const 
{
    if (!m_filenames.size())
        return String();
    // FIXME: This could be a lot faster, but assuming the data will not
    // often be much longer than the provided width, this may be fast enough.
    // If this does not need to be threadsafe, we can use crossThreadString().
    // See http://trac.webkit.org/changeset/49160.
    String output = m_filenames[0].threadsafeCopy();
    while (font.width(TextRun(output.impl())) > width && output.length() > 4)
        output = output.replace(0, 4, String("..."));
    return output;
}
예제 #16
0
static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion, TextRunFlags flags)
{
    ASSERT(style);

    TextDirection textDirection = direction;
    bool directionalOverride = style->rtlOrdering() == VisualOrder;
    if (flags != DefaultTextRunFlags) {
        if (flags & RespectDirection)
            textDirection = style->direction();
        if (flags & RespectDirectionOverride)
            directionalOverride |= isOverride(style->unicodeBidi());
    }

    return TextRun(characters, length, 0, 0, expansion, textDirection, directionalOverride);
}
예제 #17
0
LayoutUnit LayoutListMarker::getWidthOfTextWithSuffix() const {
  if (m_text.isEmpty())
    return LayoutUnit();
  const Font& font = style()->font();
  LayoutUnit itemWidth = LayoutUnit(font.width(TextRun(m_text)));
  // TODO(wkorman): Look into constructing a text run for both text and suffix
  // and painting them together.
  UChar suffix[2] = {
      ListMarkerText::suffix(style()->listStyleType(), m_listItem->value()),
      ' '};
  TextRun run =
      constructTextRun(font, suffix, 2, styleRef(), style()->direction());
  LayoutUnit suffixSpaceWidth = LayoutUnit(font.width(run));
  return itemWidth + suffixSpaceWidth;
}
예제 #18
0
TextFragmentIterator::Style::Style(const RenderStyle& style)
    : font(style.fontCascade())
    , textAlign(style.textAlign())
    , collapseWhitespace(style.collapseWhiteSpace())
    , preserveNewline(style.preserveNewline())
    , wrapLines(style.autoWrap())
    , breakAnyWordOnOverflow(style.wordBreak() == BreakAllWordBreak && wrapLines)
    , breakFirstWordOnOverflow(breakAnyWordOnOverflow || (style.breakWords() && (wrapLines || preserveNewline)))
    , breakNBSP(wrapLines && style.nbspMode() == SPACE)
    , keepAllWordsForCJK(style.wordBreak() == KeepAllWordBreak)
    , spaceWidth(font.width(TextRun(StringView(&space, 1))))
    , wordSpacing(font.wordSpacing())
    , tabWidth(collapseWhitespace ? 0 : style.tabSize())
    , locale(style.locale())
{
}
예제 #19
0
int InlineTextBox::positionForOffset(int offset) const
{
    ASSERT(offset >= m_start);
    ASSERT(offset <= m_start + m_len);

    if (isLineBreak())
        return m_x;

    RenderText* text = static_cast<RenderText*>(m_object);
    const Font& f = text->style(m_firstLine)->font();
    int from = direction() == RTL ? offset - m_start : 0;
    int to = direction() == RTL ? m_len : offset - m_start;
    // FIXME: Do we need to add rightBearing here?
    return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
                                                   IntPoint(m_x, 0), 0, from, to)).right();
}
예제 #20
0
IntRect RenderListMarker::getRelativeMarkerRect()
{
    if (isImage())
        return IntRect(m_x, m_y, m_image->imageSize().width(), m_image->imageSize().height());

    switch (style()->listStyleType()) {
        case DISC:
        case CIRCLE:
        case SQUARE: {
            // FIXME: Are these particular rounding rules necessary?
            const Font& font = style()->font();
            int ascent = font.ascent();
            int bulletWidth = (ascent * 2 / 3 + 1) / 2;
            return IntRect(m_x + 1, m_y + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
        }
        case LNONE:
            return IntRect();
        case ARMENIAN:
        case CJK_IDEOGRAPHIC:
        case DECIMAL_LEADING_ZERO:
        case GEORGIAN:
        case HEBREW:
        case HIRAGANA:
        case HIRAGANA_IROHA:
        case KATAKANA:
        case KATAKANA_IROHA:
        case LDECIMAL:
        case LOWER_ALPHA:
        case LOWER_GREEK:
        case LOWER_LATIN:
        case LOWER_ROMAN:
        case UPPER_ALPHA:
        case UPPER_LATIN:
        case UPPER_ROMAN:
            if (m_text.isEmpty())
                return IntRect();
            const Font& font = style()->font();
            int itemWidth = font.width(m_text);
            const UChar periodSpace[2] = { '.', ' ' };
            int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
            return IntRect(m_x, m_y + font.ascent(), itemWidth + periodSpaceWidth, font.height());
    }

    return IntRect();
}
예제 #21
0
void EllipsisBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font& font)
{
    Color textColor = style->color();
    Color c = m_renderer->selectionBackgroundColor();
    if (!c.isValid() || !c.alpha())
        return;

    // If the text color ends up being the same as the selection background, invert the selection
    // background.
    if (textColor == c)
        c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());

    context->save();
    int y = root()->selectionTop();
    int h = root()->selectionHeight();
    context->clip(IntRect(m_x + tx, y + ty, m_width, h));
    context->drawHighlightForText(font, TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()),
                                  IntPoint(m_x + tx, m_y + ty + y), h, c, style->colorSpace());
    context->restore();
}
예제 #22
0
void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font*, int startPos, int endPos)
{
    int offset = m_start;
    int sPos = max(startPos - offset, 0);
    int ePos = min(endPos - offset, (int)m_len);

    if (sPos >= ePos)
        return;

    context->save();

    Color c = Color(225, 221, 85);
    
    updateGraphicsContext(context, c, c, 0); // Don't draw text at all!

    int y = selectionTop();
    int h = selectionHeight();
    context->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
                            IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
    context->restore();
}
예제 #23
0
IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
{
    int sPos = max(startPos - m_start, 0);
    int ePos = min(endPos - m_start, (int)m_len);
    
    if (sPos >= ePos)
        return IntRect();

    RenderText* textObj = textObject();
    int selTop = selectionTop();
    int selHeight = selectionHeight();
    const Font& f = textObj->style(m_firstLine)->font();

    IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
                                                        IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos));
    if (r.x() > tx + m_x + m_width)
        r.setWidth(0);
    else if (r.right() - 1 > tx + m_x + m_width)
        r.setWidth(tx + m_x + m_width - r.x());
    return r;
}
예제 #24
0
void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = m_renderer->style(m_firstLine);
    Color textColor = style->color();
    if (textColor != context->fillColor())
        context->setFillColor(textColor, style->colorSpace());
    bool setShadow = false;
    if (style->textShadow()) {
        context->setShadow(IntSize(style->textShadow()->x, style->textShadow()->y),
                           style->textShadow()->blur, style->textShadow()->color, style->colorSpace());
        setShadow = true;
    }

    if (selectionState() != RenderObject::SelectionNone) {
        paintSelection(context, tx, ty, style, style->font());

        // Select the correct color for painting the text.
        Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor();
        if (foreground.isValid() && foreground != textColor)
            context->setFillColor(foreground, style->colorSpace());
    }

    const String& str = m_str;
    context->drawText(style->font(), TextRun(str.characters(), str.length(), false, 0, 0, false, style->visuallyOrdered()), IntPoint(m_x + tx, m_y + ty + style->font().ascent()));

    // Restore the regular fill color.
    if (textColor != context->fillColor())
        context->setFillColor(textColor, style->colorSpace());

    if (setShadow)
        context->clearShadow();

    if (m_markupBox) {
        // Paint the markup box
        tx += m_x + m_width - m_markupBox->x();
        ty += m_y + style->font().ascent() - (m_markupBox->y() + m_markupBox->renderer()->style(m_firstLine)->font().ascent());
        m_markupBox->paint(paintInfo, tx, ty);
    }
}
예제 #25
0
bool canUseFor(const RenderBlockFlow& flow)
{
    if (!flow.frame().settings().simpleLineLayoutEnabled())
        return false;
    if (!flow.firstChild())
        return false;
    // This currently covers <blockflow>#text</blockflow> case.
    // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
    if (flow.firstChild() != flow.lastChild())
        return false;
    if (!flow.firstChild()->isText())
        return false;
    if (!flow.isHorizontalWritingMode())
        return false;
    if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
        return false;
    if (flow.hasOutline())
        return false;
    if (flow.isRubyText() || flow.isRubyBase())
        return false;
    if (flow.parent()->isDeprecatedFlexibleBox())
        return false;
    // FIXME: Implementation of wrap=hard looks into lineboxes.
    if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr))
        return false;
    // FIXME: Placeholders do something strange.
    if (flow.parent()->isTextControl() && toRenderTextControl(*flow.parent()).textFormControlElement().placeholderElement())
        return false;
    const RenderStyle& style = flow.style();
    if (style.textDecorationsInEffect() != TextDecorationNone)
        return false;
    if (style.textAlign() == JUSTIFY)
        return false;
    // Non-visible overflow should be pretty easy to support.
    if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
        return false;
    if (!style.textIndent().isZero())
        return false;
    if (!style.wordSpacing().isZero() || style.letterSpacing())
        return false;
    if (style.textTransform() != TTNONE)
        return false;
    if (!style.isLeftToRightDirection())
        return false;
    if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
        return false;
    if (style.writingMode() != TopToBottomWritingMode)
        return false;
    if (style.lineBreak() != LineBreakAuto)
        return false;
    if (style.wordBreak() != NormalWordBreak)
        return false;
    if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
        return false;
    if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
        return false;
    if (style.hyphens() == HyphensAuto)
        return false;
    if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
        return false;
    if (style.textShadow())
        return false;
    if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()))
        return false;
    if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
        return false;
    if (style.hasTextCombine())
        return false;
    if (style.backgroundClip() == TextFillBox)
        return false;
    if (style.borderFit() == BorderFitLines)
        return false;
    const RenderText& textRenderer = toRenderText(*flow.firstChild());
    if (flow.containsFloats()) {
        // We can't use the code path if any lines would need to be shifted below floats. This is because we don't keep per-line y coordinates.
        float minimumWidthNeeded = textRenderer.minLogicalWidth();
        for (auto& floatRenderer : *flow.floatingObjectSet()) {
            ASSERT(floatRenderer);
            float availableWidth = flow.availableLogicalWidthForLine(floatRenderer->y(), false);
            if (availableWidth < minimumWidthNeeded)
                return false;
        }
    }
    if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
        || textRenderer.isSVGInlineText())
        return false;
    if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple)
        return false;
    if (style.font().isSVGFont())
        return false;

    // We assume that all lines have metrics based purely on the primary font.
    auto& primaryFontData = *style.font().primaryFont();
    if (primaryFontData.isLoading())
        return false;
    if (!canUseForText(textRenderer, primaryFontData))
        return false;

    return true;
}
    virtual void drawInContext(PlatformGraphicsContext* context)
    {
        if (!m_owner)
            return;

        CGContextSaveGState(context);

        CGRect layerBounds = bounds();
        if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
            CGContextScaleCTM(context, 1, -1);
            CGContextTranslateCTM(context, 0, -layerBounds.size.height);
        }

        if (m_owner->client()) {
            GraphicsContext graphicsContext(context);

            // It's important to get the clip from the context, because it may be significantly
            // smaller than the layer bounds (e.g. tiled layers)
            CGRect clipBounds = CGContextGetClipBoundingBox(context);
            IntRect clip(enclosingIntRect(clipBounds));
            m_owner->paintGraphicsLayerContents(graphicsContext, clip);
        }
#ifndef NDEBUG
        else {
            ASSERT_NOT_REACHED();

            // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color,
            // so CA never makes backing store for it (which is what -setNeedsDisplay will do above).
            CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f);
            CGContextFillRect(context, layerBounds);
        }
#endif

        if (m_owner->showRepaintCounter()) {
            String text = String::format("%d", m_owner->incrementRepaintCount());;

            CGContextSaveGState(context);
            CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f);
            
            CGRect aBounds = layerBounds;

            aBounds.size.width = 10 + 12 * text.length();
            aBounds.size.height = 25;
            CGContextFillRect(context, aBounds);
            
            FontDescription desc;

            NONCLIENTMETRICS metrics;
            metrics.cbSize = sizeof(metrics);
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
            FontFamily family;
            family.setFamily(metrics.lfSmCaptionFont.lfFaceName);
            desc.setFamily(family);

            desc.setComputedSize(22);
            
            Font font = Font(desc, 0, 0);
            font.update(0);

            GraphicsContext cg(context);
            cg.setFillColor(Color::black, DeviceColorSpace);
            cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 3, aBounds.origin.y + 20));

            CGContextRestoreGState(context);        
        }

        CGContextRestoreGState(context);
    }
예제 #27
0
bool canUseFor(const RenderBlockFlow& flow)
{
#if !PLATFORM(MAC) && !PLATFORM(GTK) && !PLATFORM(EFL)
    // FIXME: Non-mac platforms are hitting ASSERT(run.charactersLength() >= run.length())
    // https://bugs.webkit.org/show_bug.cgi?id=123338
    return false;
#else
    if (!flow.frame().settings().simpleLineLayoutEnabled())
        return false;
    if (!flow.firstChild())
        return false;
    // This currently covers <blockflow>#text</blockflow> case.
    // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
    if (flow.firstChild() != flow.lastChild())
        return false;
    if (!flow.firstChild()->isText())
        return false;
    if (!flow.isHorizontalWritingMode())
        return false;
    if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
        return false;
    if (flow.hasOutline())
        return false;
    if (flow.isRubyText() || flow.isRubyBase())
        return false;
    if (flow.parent()->isDeprecatedFlexibleBox())
        return false;
    // FIXME: Implementation of wrap=hard looks into lineboxes.
    if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr))
        return false;
    // FIXME: Placeholders do something strange.
    if (flow.parent()->isTextControl() && toRenderTextControl(*flow.parent()).textFormControlElement().placeholderElement())
        return false;
    // These tests only works during layout. Outside layout this function may give false positives.
    if (flow.view().layoutState()) {
#if ENABLE(CSS_SHAPES)
        if (flow.view().layoutState()->shapeInsideInfo())
            return false;
#endif
        if (flow.view().layoutState()->m_columnInfo)
            return false;
    }
    const RenderStyle& style = flow.style();
    if (style.textDecorationsInEffect() != TextDecorationNone)
        return false;
    if (style.textAlign() == JUSTIFY)
        return false;
    // Non-visible overflow should be pretty easy to support.
    if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
        return false;
    if (!style.textIndent().isZero())
        return false;
    if (style.wordSpacing() || style.letterSpacing())
        return false;
    if (style.textTransform() != TTNONE)
        return false;
    if (!style.isLeftToRightDirection())
        return false;
    if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
        return false;
    if (style.writingMode() != TopToBottomWritingMode)
        return false;
    if (style.lineBreak() != LineBreakAuto)
        return false;
    if (style.wordBreak() != NormalWordBreak)
        return false;
    if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
        return false;
    if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
        return false;
    if (style.hyphens() == HyphensAuto)
        return false;
    if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
        return false;
    if (style.textShadow())
        return false;
#if ENABLE(CSS_SHAPES)
    if (style.resolvedShapeInside())
        return true;
#endif
    if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()))
        return false;
    if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
        return false;
    if (style.hasTextCombine())
        return false;
    if (style.backgroundClip() == TextFillBox)
        return false;
    if (style.borderFit() == BorderFitLines)
        return false;
    const RenderText& textRenderer = toRenderText(*flow.firstChild());
    if (flow.containsFloats()) {
        // We can't use the code path if any lines would need to be shifted below floats. This is because we don't keep per-line y coordinates.
        // It is enough to test the first line width only as currently all floats must be overhanging.
        if (textRenderer.minLogicalWidth() > LineWidth(const_cast<RenderBlockFlow&>(flow), false, DoNotIndentText).availableWidth())
            return false;
    }
    if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
#if ENABLE(SVG)
        || textRenderer.isSVGInlineText()
#endif
        )
        return false;
    if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple)
        return false;
    if (style.font().isSVGFont())
        return false;

    // We assume that all lines have metrics based purely on the primary font.
    auto& primaryFontData = *style.font().primaryFont();
    if (primaryFontData.isLoading())
        return false;
    if (!canUseForText(textRenderer, primaryFontData))
        return false;

    return true;
#endif
}
예제 #28
0
bool canUseFor(const RenderBlockFlow& flow)
{
#if !PLATFORM(MAC)
    // FIXME: Non-mac platforms are hitting ASSERT(run.charactersLength() >= run.length())
    // https://bugs.webkit.org/show_bug.cgi?id=123338
    return false;
#endif
    if (!flow.firstChild())
        return false;
    // This currently covers <blockflow>#text</blockflow> case.
    // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
    if (flow.firstChild() != flow.lastChild())
        return false;
    if (!flow.firstChild()->isText())
        return false;
    // Supporting floats would be very beneficial.
    if (flow.containsFloats())
        return false;
    if (!flow.isHorizontalWritingMode())
        return false;
    if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
        return false;
    if (flow.hasOutline())
        return false;
    if (flow.isRubyText() || flow.isRubyBase())
        return false;
    // These tests only works during layout. Outside layout this function may give false positives.
    if (flow.view().layoutState()) {
#if ENABLE(CSS_SHAPES)
        if (flow.view().layoutState()->shapeInsideInfo())
            return false;
#endif
        if (flow.view().layoutState()->m_columnInfo)
            return false;
    }
    const RenderStyle& style = *flow.style();
    // It shoudn't be hard to support other alignments.
    if (style.textAlign() != LEFT && style.textAlign() != WEBKIT_LEFT && style.textAlign() != TASTART)
        return false;
    // Non-visible overflow should be pretty easy to support.
    if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
        return false;
    // Pre/no-wrap would be very helpful to support.
    if (style.whiteSpace() != NORMAL)
        return false;
    if (!style.textIndent().isZero())
        return false;
    if (style.wordSpacing() || style.letterSpacing())
        return false;
    if (style.textTransform() != TTNONE)
        return false;
    if (!style.isLeftToRightDirection())
        return false;
    if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
        return false;
    if (style.writingMode() != TopToBottomWritingMode)
        return false;
    if (style.lineBreak() != LineBreakAuto)
        return false;
    if (style.wordBreak() != NormalWordBreak)
        return false;
    if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
        return false;
    if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
        return false;
    if (style.hyphens() == HyphensAuto)
        return false;
    if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
        return false;
    if (style.textShadow())
        return false;
#if ENABLE(CSS_SHAPES)
    if (style.resolvedShapeInside())
        return true;
#endif
    if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style()->textOverflow()))
        return false;
    if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
        return false;
    if (style.hasTextCombine())
        return false;
    if (style.overflowWrap() != NormalOverflowWrap)
        return false;
    if (style.backgroundClip() == TextFillBox)
        return false;
    if (style.borderFit() == BorderFitLines)
        return false;
    const RenderText& textRenderer = toRenderText(*flow.firstChild());
    if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
#if ENABLE(SVG)
        || textRenderer.isSVGInlineText()
#endif
        )
        return false;
    if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple)
        return false;

    auto primaryFontData = style.font().primaryFont();

    unsigned length = textRenderer.textLength();
    unsigned consecutiveSpaceCount = 0;
    for (unsigned i = 0; i < length; ++i) {
        // This rejects anything with more than one consecutive whitespace, except at the beginning or end.
        // This is because we don't currently do subruns within lines. Fixing this would improve coverage significantly.
        UChar character = textRenderer.characterAt(i);
        if (isWhitespace(character))
            ++consecutiveSpaceCount;
        else {
            if (consecutiveSpaceCount != i && consecutiveSpaceCount > 1)
                return false;
            consecutiveSpaceCount = 0;
        }
        // These would be easy to support.
        if (character == noBreakSpace)
            return false;
        if (character == softHyphen)
            return false;

        static const UChar lowestRTLCharacter = 0x590;
        if (character >= lowestRTLCharacter) {
            UCharDirection direction = u_charDirection(character);
            if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
                || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
                || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE)
                return false;
        }
        if (!primaryFontData->glyphForCharacter(character))
            return false;
    }
    return true;
}
예제 #29
0
std::unique_ptr<Lines> createLines(RenderBlockFlow& flow)
{
    auto lines = std::make_unique<Lines>();

    RenderText& textRenderer = toRenderText(*flow.firstChild());
    ASSERT(!textRenderer.firstTextBox());

    const RenderStyle& style = *flow.style();
    const unsigned textLength = textRenderer.textLength();

    float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));

    LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
    int nextBreakable = -1;

    unsigned lineEndOffset = 0;
    while (lineEndOffset < textLength) {
        lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
        unsigned lineStartOffset = lineEndOffset;
        unsigned runEndOffset = lineEndOffset;
        LineWidth lineWidth(flow, false, DoNotIndentText);
        while (runEndOffset < textLength) {
            ASSERT(!isWhitespace(textRenderer.characterAt(runEndOffset)));

            bool previousWasSpaceBetweenRuns = runEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(runEndOffset - 1));
            unsigned runStartOffset = previousWasSpaceBetweenRuns ? runEndOffset - 1 : runEndOffset;

            ++runEndOffset;
            while (runEndOffset < textLength) {
                if (runEndOffset > lineStartOffset && isBreakable(lineBreakIterator, runEndOffset, nextBreakable, false))
                    break;
                ++runEndOffset;
            }

            unsigned runLength = runEndOffset - runStartOffset;
            bool includeEndSpace = runEndOffset < textLength && textRenderer.characterAt(runEndOffset) == ' ';
            float wordWidth;
            if (includeEndSpace)
                wordWidth = textWidth(textRenderer, runStartOffset, runLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
            else
                wordWidth = textWidth(textRenderer, runStartOffset, runLength, lineWidth.committedWidth(), style);

            lineWidth.addUncommittedWidth(wordWidth);
            if (!lineWidth.fitsOnLine()) {
                if (!lineWidth.committedWidth()) {
                    lineWidth.commit();
                    lineEndOffset = runEndOffset;
                }
                break;
            }
            lineWidth.commit();
            lineEndOffset = runEndOffset;
            runEndOffset = skipWhitespaces(textRenderer, runEndOffset, textLength);
        }
        if (lineStartOffset == lineEndOffset)
            continue;

        Line line;
        line.textOffset = lineStartOffset;
        line.textLength = lineEndOffset - lineStartOffset;
        line.width = lineWidth.committedWidth();

        lines->append(line);
    }

    textRenderer.clearNeedsLayout();

    lines->shrinkToFit();
    return lines;
}
예제 #30
0
WebTextRun::operator WebCore::TextRun() const
{
    return TextRun(text, 0, 0, TextRun::AllowTrailingExpansion, rtl ? RTL : LTR, directionalOverride);
}