Skip to content

Commit

Permalink
Merge pull request #219 from FinalCAD/use_generic_html_format_tag
Browse files Browse the repository at this point in the history
Export html with tags instead of styling when applicable
  • Loading branch information
MohamedRejeb authored Mar 24, 2024
2 parents 82db1a1 + b3ba8a9 commit 964839a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.isUnspecified
import androidx.compose.ui.unit.sp
import com.mohamedrejeb.richeditor.parser.utils.MARK_BACKGROUND_COLOR
import com.mohamedrejeb.richeditor.parser.utils.SMALL_FONT_SIZE
import com.mohamedrejeb.richeditor.utils.maxDecimals
import kotlin.math.roundToInt

Expand All @@ -38,42 +39,76 @@ internal object CssDecoder {
* @param spanStyle the span style to decode.
* @return the decoded CSS style map.
*/
internal fun decodeSpanStyleToCssStyleMap(spanStyle: SpanStyle): Map<String, String> {
internal fun decodeSpanStyleToHtmlStylingFormat(spanStyle: SpanStyle): HtmlStylingFormat {
val cssStyleMap = mutableMapOf<String, String>()
val htmlTags = mutableListOf<String>()

if (spanStyle.color.isSpecified) {
cssStyleMap["color"] = decodeColorToCss(spanStyle.color)
}
if (spanStyle.fontSize.isSpecified) {
decodeTextUnitToCss(spanStyle.fontSize)?.let { fontSize ->
cssStyleMap["font-size"] = fontSize
if (spanStyle.fontSize == SMALL_FONT_SIZE) {
htmlTags.add("small")
} else {
decodeTextUnitToCss(spanStyle.fontSize)?.let { fontSize ->
cssStyleMap["font-size"] = fontSize
}
}
}
spanStyle.fontWeight?.let { fontWeight ->
cssStyleMap["font-weight"] = decodeFontWeightToCss(fontWeight)
if (fontWeight == FontWeight.Bold) {
htmlTags.add("b")
} else {
cssStyleMap["font-weight"] = decodeFontWeightToCss(fontWeight)
}
}
spanStyle.fontStyle?.let { fontStyle ->
cssStyleMap["font-style"] = decodeFontStyleToCss(fontStyle)
if (fontStyle == FontStyle.Italic) {
htmlTags.add("i")
} else {
cssStyleMap["font-style"] = decodeFontStyleToCss(fontStyle)
}
}
if (spanStyle.letterSpacing.isSpecified) {
decodeTextUnitToCss(spanStyle.letterSpacing)?.let { letterSpacing ->
cssStyleMap["letter-spacing"] = letterSpacing
}
}
spanStyle.baselineShift?.let { baselineShift ->
cssStyleMap["baseline-shift"] = decodeBaselineShiftToCss(baselineShift)
when (baselineShift) {
BaselineShift.Subscript -> htmlTags.add("sub")
BaselineShift.Superscript -> htmlTags.add("sup")
else -> cssStyleMap["baseline-shift"] = decodeBaselineShiftToCss(baselineShift)
}
}
if (spanStyle.background.isSpecified) {
cssStyleMap["background"] = decodeColorToCss(spanStyle.background)
if (spanStyle.background == MARK_BACKGROUND_COLOR) {
htmlTags.add("mark")
} else {
cssStyleMap["background"] = decodeColorToCss(spanStyle.background)
}
}
spanStyle.textDecoration?.let { textDecoration ->
cssStyleMap["text-decoration"] = decodeTextDecorationToCss(textDecoration)
when (textDecoration) {
TextDecoration.Underline -> htmlTags.add("u")
TextDecoration.LineThrough -> htmlTags.add("s")
TextDecoration.Underline + TextDecoration.LineThrough -> {
htmlTags.add("u")
htmlTags.add("s")
}

else -> cssStyleMap["text-decoration"] = decodeTextDecorationToCss(textDecoration)
}

}
spanStyle.shadow?.let { shadow ->
cssStyleMap["text-shadow"] = decodeTextShadowToCss(shadow)
}

return cssStyleMap
return HtmlStylingFormat(
htmlTags = htmlTags,
cssStyleMap = cssStyleMap,
)
}

/**
Expand Down Expand Up @@ -260,4 +295,8 @@ internal object CssDecoder {
}
}

data class HtmlStylingFormat(
val htmlTags: List<String>,
val cssStyleMap: Map<String, String>,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ internal object RichTextStateHtmlParser : RichTextStateParser<String> {
val paragraphCss = CssDecoder.decodeCssStyleMap(paragraphCssMap)

// Append paragraph opening tag
builder.append("<$paragraphTagName style=\"$paragraphCss\">")
builder.append("<$paragraphTagName")
if (paragraphCss.isNotBlank()) builder.append(" style=\"$paragraphCss\"")
builder.append(">")

// Append paragraph children
richParagraph.children.fastForEach { richSpan ->
Expand All @@ -236,7 +238,7 @@ internal object RichTextStateHtmlParser : RichTextStateParser<String> {
return builder.toString()
}

private fun decodeRichSpanToHtml(richSpan: RichSpan): String {
private fun decodeRichSpanToHtml(richSpan: RichSpan, parentFormattingTags: List<String> = emptyList()): String {
val stringBuilder = StringBuilder()

// Check if span is empty
Expand All @@ -254,8 +256,9 @@ internal object RichTextStateHtmlParser : RichTextStateParser<String> {
}

// Convert span style to CSS string
val spanCssMap = CssDecoder.decodeSpanStyleToCssStyleMap(richSpan.spanStyle)
val spanCss = CssDecoder.decodeCssStyleMap(spanCssMap)
val htmlStyleFormat = CssDecoder.decodeSpanStyleToHtmlStylingFormat(richSpan.spanStyle)
val spanCss = CssDecoder.decodeCssStyleMap(htmlStyleFormat.cssStyleMap)
val htmlTags = htmlStyleFormat.htmlTags.filter { it !in parentFormattingTags }

val isRequireOpeningTag = tagName != "span" || tagAttributes.isNotEmpty() || spanCss.isNotEmpty()

Expand All @@ -266,12 +269,25 @@ internal object RichTextStateHtmlParser : RichTextStateParser<String> {
stringBuilder.append(">")
}

htmlTags.forEach {
stringBuilder.append("<$it>")
}

// Append text
stringBuilder.append(KsoupEntities.encodeHtml(richSpan.text))

// Append children
richSpan.children.fastForEach { child ->
stringBuilder.append(decodeRichSpanToHtml(child))
stringBuilder.append(
decodeRichSpanToHtml(
richSpan = child,
parentFormattingTags = parentFormattingTags + htmlTags,
)
)
}

htmlTags.reversed().forEach {
stringBuilder.append("</$it>")
}

if (isRequireOpeningTag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.em

internal val MARK_BACKGROUND_COLOR = Color.Yellow
internal val SMALL_FONT_SIZE = 0.8f.em

internal val BoldSpanStyle = SpanStyle(fontWeight = FontWeight.Bold)
internal val ItalicSpanStyle = SpanStyle(fontStyle = FontStyle.Italic)
internal val UnderlineSpanStyle = SpanStyle(textDecoration = TextDecoration.Underline)
internal val StrikethroughSpanStyle = SpanStyle(textDecoration = TextDecoration.LineThrough)
internal val SubscriptSpanStyle = SpanStyle(baselineShift = BaselineShift.Subscript)
internal val SuperscriptSpanStyle = SpanStyle(baselineShift = BaselineShift.Superscript)
internal val MarkSpanStyle = SpanStyle(background = Color.Yellow)
internal val SmallSpanStyle = SpanStyle(fontSize = 0.8f.em)
internal val MarkSpanStyle = SpanStyle(background = MARK_BACKGROUND_COLOR)
internal val SmallSpanStyle = SpanStyle(fontSize = SMALL_FONT_SIZE)
internal val H1SPanStyle = SpanStyle(fontSize = 2.em, fontWeight = FontWeight.Bold)
internal val H2SPanStyle = SpanStyle(fontSize = 1.5.em, fontWeight = FontWeight.Bold)
internal val H3SPanStyle = SpanStyle(fontSize = 1.17.em, fontWeight = FontWeight.Bold)
Expand Down

0 comments on commit 964839a

Please sign in to comment.