Beispiel #1
0
bool
ParagraphLayout::_AppendGlyphInfos(const TextSpan& span)
{
	int charCount = span.CountChars();
	if (charCount == 0)
		return true;

	const BString& text = span.Text();
	const BFont& font = span.Style().Font();

	// Allocate arrays
	float* escapementArray = new (std::nothrow) float[charCount];
	if (escapementArray == NULL)
		return false;
	ArrayDeleter<float> escapementDeleter(escapementArray);

	// Fetch glyph spacing information
	font.GetEscapements(text, charCount, escapementArray);

	// Append to glyph buffer and convert escapement scale
	float size = font.Size();
	const char* c = text.String();
	for (int i = 0; i < charCount; i++) {
		if (!_AppendGlyphInfo(UTF8ToCharCode(&c), escapementArray[i] * size,
				span.Style())) {
			return false;
		}
	}

	return true;
}
Beispiel #2
0
void
ParagraphLayout::_DrawSpan(BView* view, BPoint offset,
	const TextSpan& span, int32 textOffset) const
{
	const BString& text = span.Text();
	if (text.Length() == 0)
		return;

	const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset);
	const LineInfo& line = fLineInfos.ItemAtFast(glyph.lineIndex);

	offset.x += glyph.x;
	offset.y += line.y + line.maxAscent;

	const CharacterStyle& style = span.Style();

	view->SetFont(&style.Font());
	view->SetHighColor(style.ForegroundColor());

	// TODO: Implement other style properties

	escapement_delta delta;
	delta.nonspace = line.extraGlyphSpacing;
	delta.space = line.extraWhiteSpacing;

	// TODO: Fix in app_server: First glyph should not be shifted by delta.
	if (text[0] == ' ')
		offset.x -= delta.space;
	else
		offset.x -= delta.nonspace;

	view->DrawString(span.Text(), offset, &delta);
}
Beispiel #3
0
bool
Paragraph::Append(const TextSpan& span)
{
	// Try to merge with last span if the TextStyles are equal
	if (fTextSpans.CountItems() > 0) {
		const TextSpan& lastSpan = fTextSpans.LastItem();
		if (lastSpan.Style() == span.Style()) {
			BString text(lastSpan.Text());
			text.Append(span.Text());
			fTextSpans.Remove();
			return fTextSpans.Add(TextSpan(text, span.Style()));
		}
	}
	return fTextSpans.Add(span);
}
Beispiel #4
0
bool
Paragraph::Prepend(const TextSpan& span)
{
	_InvalidateCachedLength();

	// Try to merge with first span if the TextStyles are equal
	if (fTextSpans.CountItems() > 0) {
		const TextSpan& firstSpan = fTextSpans.ItemAtFast(0);
		if (firstSpan.Style() == span.Style()) {
			BString text(span.Text());
			text.Append(firstSpan.Text());
			return fTextSpans.Replace(0, TextSpan(text, span.Style()));
		}
	}
	return fTextSpans.Add(span, 0);
}
Beispiel #5
0
void
ParagraphLayout::_DrawSpan(BView* view, const TextSpan& span,
	int32 textOffset) const
{
	const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset);
	const LineInfo& line = fLineInfos.ItemAtFast(glyph.lineIndex);
	
	float x = glyph.x;
	float y = line.y + line.maxAscent;

	const CharacterStyle& style = span.Style();

	view->SetFont(&style.Font());
	view->SetHighColor(style.ForegroundColor());

	// TODO: Implement other style properties
	// TODO: Implement correct glyph spacing

	view->DrawString(span.Text(), BPoint(x, y));
}
Beispiel #6
0
void
TextBuffer::insertSpan( std::string s, TextSpan &span ) { 

	startEdit( span.begin, span.end );

	if ( !span.empty() ) removeSpan(span);

	for ( t_CKUINT i=0; i < s.size(); i++) { 
		if ( s[i] == '\n' ) insertLine( span.end );
		else insertChar( s[i], span.end );
	}

	endEdit ( span.begin, span.end );
}
Beispiel #7
0
void
TextContent::paste(string s, TextSpan &span) { 
    if ( !span.empty() ) _buf->removeSpan(span);
    _buf->insertSpan( s, span );
    _buf->closeEdit();
}
Beispiel #8
0
bool
Paragraph::Insert(int32 offset, const TextSpan& newSpan)
{
	int32 index = 0;
	while (index < fTextSpans.CountItems()) {
		const TextSpan& span = fTextSpans.ItemAtFast(index);
		if (offset - span.CharCount() < 0)
			break;
		offset -= span.CharCount();
	}

	if (fTextSpans.CountItems() == index)
		return Append(newSpan);

	// Try to merge with span at index if the TextStyles are equal
	TextSpan span = fTextSpans.ItemAtFast(index);
	if (span.Style() == newSpan.Style()) {
		span.Insert(offset, newSpan.Text());
		return fTextSpans.Replace(index, span);
	}

	if (offset == 0) {
		if (index > 0) {
			// Try to merge with TextSpan before if offset == 0 && index > 0
			TextSpan span = fTextSpans.ItemAtFast(index - 1);
			if (span.Style() == newSpan.Style()) {
				span.Insert(span.CharCount(), newSpan.Text());
				return fTextSpans.Replace(index - 1, span);
			}
		}
		// Just insert the new span before the one at index
		return fTextSpans.Add(newSpan, index);
	}

	// Split the span, 
	TextSpan spanBefore = span.SubSpan(0, offset);
	TextSpan spanAfter = span.SubSpan(offset, span.CharCount() - offset);

	return fTextSpans.Replace(index, spanBefore)
		&& fTextSpans.Add(newSpan, index + 1)
		&& fTextSpans.Add(spanAfter, index + 2);
}
Beispiel #9
0
bool
Paragraph::Remove(int32 offset, int32 length)
{
	if (length == 0)
		return true;

	_InvalidateCachedLength();

	int32 index = 0;
	while (index < fTextSpans.CountItems()) {
		const TextSpan& span = fTextSpans.ItemAtFast(index);
		if (offset - span.CountChars() < 0)
			break;
		offset -= span.CountChars();
		index++;
	}

	if (index >= fTextSpans.CountItems())
		return false;

	TextSpan span(fTextSpans.ItemAtFast(index));
	int32 removeLength = std::min(span.CountChars() - offset, length);
	span.Remove(offset, removeLength);
	length -= removeLength;
	index += 1;

	// Remove more spans if necessary
	while (length > 0 && index < fTextSpans.CountItems()) {
		int32 spanLength = fTextSpans.ItemAtFast(index).CountChars();
		if (spanLength <= length) {
			fTextSpans.Remove(index);
			length -= spanLength;
		} else {
			// Reached last span
			removeLength = std::min(length, spanLength);
			TextSpan lastSpan = fTextSpans.ItemAtFast(index).SubSpan(
				removeLength, spanLength - removeLength);
			// Try to merge with first span, otherwise replace span at index
			if (lastSpan.Style() == span.Style()) {
				span.Insert(span.CountChars(), lastSpan.Text());
				fTextSpans.Remove(index);
			} else {
				fTextSpans.Replace(index, lastSpan);
			}

			break;
		}
	}

	// See if anything from the TextSpan at offset remained, keep it as empty
	// span if it is the last remaining span.
	index--;
	if (span.CountChars() > 0 || fTextSpans.CountItems() == 1) {
		fTextSpans.Replace(index, span);
	} else {
		fTextSpans.Remove(index);
		index--;
	}

	// See if spans can be merged after one has been removed.
	if (index >= 0 && index + 1 < fTextSpans.CountItems()) {
		const TextSpan& span1 = fTextSpans.ItemAtFast(index);
		const TextSpan& span2 = fTextSpans.ItemAtFast(index + 1);
		if (span1.Style() == span2.Style()) {
			span = span1;
			span.Append(span2.Text());
			fTextSpans.Replace(index, span);
			fTextSpans.Remove(index + 1);
		}
	}

	return true;
}
Beispiel #10
0
void TextLayout::process(const FontSet &fontSet, const TextSpan &run)
{
    auto buffer = hb_buffer_create();
    map<uint32_t, Cluster> clusterMap;
    
    for (auto font : fontSet)
    {
        hb_buffer_clear_contents(buffer);
        
        run.apply(buffer);
        hb_shape(font->hbFont, buffer, NULL, 0);
        
        auto glyphCount = hb_buffer_get_length(buffer);
        auto glyphInfos = hb_buffer_get_glyph_infos(buffer, NULL);
        auto glyphPositions = hb_buffer_get_glyph_positions(buffer, NULL);
        
        bool hasMissingGlyphs = false;
        
        for (int i = 0; i < glyphCount; i++)
        {
            auto codepoint = glyphInfos[i].codepoint;
            auto cluster = glyphInfos[i].cluster;
            
            auto it = clusterMap.find(cluster);
            bool clusterFound = (it != clusterMap.end());
            
            if (codepoint)
            {
                if (clusterFound && (it->second.font != font))
                {
                    continue; // CLUSTER FOUND, WITH ANOTHER FONT (E.G. SPACE)
                }
                else
                {
                    auto offset = Vec2f(glyphPositions[i].x_offset, -glyphPositions[i].y_offset) * font->scale;
                    float advance = glyphPositions[i].x_advance * font->scale.x;
                    
                    if (clusterFound)
                    {
                        it->second.addShape(codepoint, offset, advance);
                    }
                    else
                    {
                        clusterMap.insert(make_pair(cluster, Cluster(font, codepoint, offset, advance)));
                    }
                }
            }
            else if (!clusterFound)
            {
                hasMissingGlyphs = true;
            }
        }
        
        if (!hasMissingGlyphs)
        {
            break; // NO NEED TO PROCEED TO THE NEXT FONT IN THE LIST
        }
    }
    
    if (run.direction == HB_DIRECTION_RTL)
    {
        for (auto it = clusterMap.rbegin(); it != clusterMap.rend(); ++it)
        {
            addCluster(it->second);
        }
    }
    else
    {
        for (auto it = clusterMap.begin(); it != clusterMap.end(); ++it)
        {
            addCluster(it->second);
        }
    }
    
    hb_buffer_destroy(buffer);

}