Can now extract formatted HTML from rich textbox

This commit is contained in:
Yousef Khadadeh 2025-11-11 11:46:02 +03:00
parent c2e3fd553e
commit 9cdac48374
3 changed files with 147 additions and 14 deletions

View file

@ -101,8 +101,6 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]);
}
t.gui.pop_id();
if(a->textBox->inputChangedTextBox)

View file

@ -3,6 +3,7 @@
#include <limits>
#include <modules/skparagraph/include/DartTypes.h>
#include <modules/skparagraph/include/Metrics.h>
#include <modules/skparagraph/include/TextStyle.h>
#include <modules/skparagraph/src/TextLine.h>
#include <modules/skunicode/include/SkUnicode.h>
#include <modules/skunicode/include/SkUnicode_icu.h>
@ -14,6 +15,96 @@
namespace RichText {
std::string TextData::get_html() {
auto nextTStyleModIt = tStyleMods.begin();
skia::textlayout::TextStyle tStyle;
std::stringstream toRet;
for(size_t pIndex = 0; pIndex < paragraphs.size(); pIndex++) {
Paragraph& pData = paragraphs[pIndex];
size_t tIndex = 0;
toRet << "<p>";
for(;;) {
if(nextTStyleModIt == tStyleMods.end() || nextTStyleModIt->pos.fParagraphIndex != pIndex) {
if(pData.text.length() != tIndex) {
toRet << "<span style=\"" << get_css_from_textstyle(tStyle) << "\">"
<< pData.text.substr(tIndex, pData.text.length() - tIndex)
<< "</span>";
}
break;
}
else {
auto& nextTStyleMod = *nextTStyleModIt;
// Print with previous style
if(nextTStyleMod.pos.fTextByteIndex != tIndex) {
toRet << "<span style=\"" << get_css_from_textstyle(tStyle) << "\">"
<< pData.text.substr(tIndex, nextTStyleMod.pos.fTextByteIndex - tIndex)
<< "</span>";
}
tIndex = nextTStyleMod.pos.fTextByteIndex;
// Modify tStyle with new data
for(auto& [modType, modifier] : nextTStyleMod.mods)
modifier->modify_text_style(tStyle);
++nextTStyleModIt;
}
}
toRet << "</p>";
}
return toRet.str();
}
std::string TextData::get_css_from_textstyle(const skia::textlayout::TextStyle& tStyle) {
std::stringstream toRet;
toRet << "font: " << tStyle.getFontSize() << "px ";
auto& fontFamilies = tStyle.getFontFamilies();
for(size_t i = 0; i < fontFamilies.size(); i++) {
auto& family = fontFamilies[i];
toRet << std::string(family.c_str(), family.size());
if(i != fontFamilies.size() - 1)
toRet << ",";
}
SkPaint paint = tStyle.getForeground();
toRet << ";"
<< "color: rgb(" << static_cast<int>(paint.getColor4f().fR * 255.0f) << " " << static_cast<int>(paint.getColor4f().fG * 255.0f) << " " << static_cast<int>(paint.getColor4f().fB * 255.0f) << " / " << paint.getColor4f().fA << ");"
<< "font-weight:" << tStyle.getFontStyle().weight() << "; "
<< "font-style:";
switch(tStyle.getFontStyle().slant()) {
case SkFontStyle::Slant::kUpright_Slant:
toRet << "normal";
break;
case SkFontStyle::Slant::kItalic_Slant:
toRet << "italic";
break;
case SkFontStyle::Slant::kOblique_Slant:
toRet << "oblique";
break;
}
toRet << ";"
<< "text-decoration-line:";
if(tStyle.getDecorationType() == skia::textlayout::TextDecoration::kNoDecoration)
toRet << " none";
else {
if(tStyle.getDecorationType() & skia::textlayout::TextDecoration::kOverline)
toRet << " overline";
if(tStyle.getDecorationType() & skia::textlayout::TextDecoration::kUnderline)
toRet << " underline";
if(tStyle.getDecorationType() & skia::textlayout::TextDecoration::kLineThrough)
toRet << " line-through";
}
toRet << ";";
return toRet.str();
}
void TextData::save(cereal::PortableBinaryOutputArchive& a) const {
a(paragraphs);
a(static_cast<uint32_t>(tStyleMods.size()));
@ -280,6 +371,43 @@ TextData TextBox::get_rich_text_data() {
return toRet;
}
TextData TextBox::get_rich_text_data_between(TextPosition p1, TextPosition p2) {
auto [start, end] = get_start_end_text_pos(p1, p2);
TextData toRet;
if(start == end) {
toRet.paragraphs.emplace_back();
return toRet;
}
for(size_t pIndex = start.fParagraphIndex; pIndex <= end.fParagraphIndex; pIndex++) {
size_t textIndexStart = pIndex == start.fParagraphIndex ? start.fTextByteIndex : 0;
size_t textIndexEnd = pIndex == end.fParagraphIndex ? end.fTextByteIndex : paragraphs[pIndex].text.size();
toRet.paragraphs.emplace_back();
toRet.paragraphs.back().text = paragraphs[pIndex].text.substr(textIndexStart, textIndexEnd - textIndexStart);
toRet.paragraphs.back().pStyleData = paragraphs[pIndex].pStyleData;
}
toRet.tStyleMods.emplace_back(TextPosition{0, 0}, get_mods_used_at_pos(p1));
for(auto& [modPos, modifiers] : tStyleMods) {
if(modPos <= p1)
continue;
else if(modPos > p2)
break;
TextPosition insertPos;
insertPos.fParagraphIndex = modPos.fParagraphIndex - p1.fParagraphIndex;
if(modPos.fParagraphIndex == p1.fParagraphIndex)
insertPos.fTextByteIndex = modPos.fTextByteIndex - p1.fTextByteIndex;
else
insertPos.fTextByteIndex = modPos.fTextByteIndex;
toRet.tStyleMods.emplace_back(insertPos, modifiers);
}
return toRet;
}
void TextBox::clear_text() {
paragraphs.clear();
tStyleMods.clear();

View file

@ -33,18 +33,22 @@ struct PositionedTextStyleMod {
typedef std::vector<PositionedTextStyleMod> TextStyleModContainer;
struct TextData {
struct Paragraph {
std::string text;
ParagraphStyleData pStyleData;
template<typename Archive> void serialize(Archive& a) {
a(text, pStyleData);
}
};
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
TextStyleModContainer tStyleMods;
std::vector<Paragraph> paragraphs;
class TextData {
public:
struct Paragraph {
std::string text;
ParagraphStyleData pStyleData;
template<typename Archive> void serialize(Archive& a) {
a(text, pStyleData);
}
};
std::string get_html();
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
TextStyleModContainer tStyleMods;
std::vector<Paragraph> paragraphs;
private:
std::string get_css_from_textstyle(const skia::textlayout::TextStyle& tStyle);
};
class TextBox {
@ -116,6 +120,9 @@ class TextBox {
TextData get_rich_text_data();
void set_rich_text_data(const TextData& richText);
TextData get_rich_text_data_between(TextPosition p1, TextPosition p2);
void clear_text();
void set_string(const std::string& str);