Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -527,11 +527,11 @@ class EnrichedTextInputView : AppCompatEditText {
parametrizedStyles?.setLinkSpan(start, end, text, url)
}

fun addImage(src: String) {
fun addImage(src: String, width: Float, height: Float) {
val isValid = verifyStyle(EnrichedSpans.IMAGE)
if (!isValid) return

parametrizedStyles?.setImageSpan(src)
parametrizedStyles?.setImageSpan(src, width, height)
layoutManager.invalidateLayout()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ class EnrichedTextInputViewManager : SimpleViewManager<EnrichedTextInputView>(),
view?.addLink(start, end, text, url)
}

override fun addImage(view: EnrichedTextInputView?, src: String) {
view?.addImage(src)
override fun addImage(view: EnrichedTextInputView?, src: String, width: Float, height: Float) {
view?.addImage(src, width, height)
}

override fun startMention(view: EnrichedTextInputView?, indicator: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package com.swmansion.enriched.spans

import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.Drawable
import android.net.Uri
import android.text.style.ImageSpan
import androidx.core.graphics.withSave
import com.swmansion.enriched.spans.interfaces.EnrichedInlineSpan
import com.swmansion.enriched.styles.HtmlStyle

class EnrichedImageSpan : ImageSpan, EnrichedInlineSpan {
private var htmlStyle: HtmlStyle? = null
private var width: Int = 0
private var height: Int = 0

constructor(context: Context, uri: Uri, htmlStyle: HtmlStyle, ) : super(context, uri, ALIGN_BASELINE) {
this.htmlStyle = htmlStyle
constructor(context: Context, uri: Uri, width: Int, height: Int) : super(context, uri, ALIGN_BASELINE) {
this.width = width
this.height = height
}

constructor(drawable: Drawable, source: String, htmlStyle: HtmlStyle) : super(drawable, source, ALIGN_BASELINE) {
this.htmlStyle = htmlStyle
constructor(drawable: Drawable, source: String, width: Int, height: Int) : super(drawable, source, ALIGN_BASELINE) {
this.width = width
this.height = height
}

override fun draw(
Expand All @@ -35,7 +38,17 @@ class EnrichedImageSpan : ImageSpan, EnrichedInlineSpan {

override fun getDrawable(): Drawable {
val drawable = super.getDrawable()
drawable.setBounds(0, 0, htmlStyle!!.imgWidth, htmlStyle!!.imgHeight)
val scale = Resources.getSystem().displayMetrics.density

drawable.setBounds(0, 0, (width * scale).toInt() , (height * scale).toInt())
Comment thread
kacperzolkiewski marked this conversation as resolved.
return drawable
}

fun getWidth(): Int {
return width
}

fun getHeight(): Int {
return height
}
}
11 changes: 2 additions & 9 deletions android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ class HtmlStyle {
var ulBulletSize: Int = 8
var ulBulletColor: Int = Color.BLACK

var imgWidth: Int = 200
var imgHeight: Int = 200

var aColor: Int = Color.BLACK
var aUnderline: Boolean = true

Expand Down Expand Up @@ -100,10 +97,6 @@ class HtmlStyle {
ulMarginLeft = parseFloat(ulStyle, "marginLeft").toInt()
ulBulletSize = parseFloat(ulStyle, "bulletSize").toInt()

val imgStyle = style.getMap("img")
imgWidth = parseFloat(imgStyle, "width").toInt()
imgHeight = parseFloat(imgStyle, "height").toInt()

val aStyle = style.getMap("a")
aColor = parseColor(aStyle, "color")
aUnderline = parseIsUnderline(aStyle)
Expand All @@ -124,8 +117,8 @@ class HtmlStyle {
private fun parseFloat(map: ReadableMap?, key: String): Float {
val safeMap = ensureValueIsSet(map, key)

val fontSize = safeMap.getDouble(key)
return ceil(PixelUtil.toPixelFromSP(fontSize))
val value = safeMap.getDouble(key)
return ceil(PixelUtil.toPixelFromSP(value))
}

private fun parseColorWithOpacity(map: ReadableMap?, key: String, opacity: Int): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,26 +170,28 @@ class ParametrizedStyles(private val view: EnrichedTextInputView) {
}
}

fun setImageSpan(src: String) {
fun setImageSpan(src: String, width: Float, height: Float) {
if (view.selection == null) return

val spannable = view.text as SpannableStringBuilder
var (start, end) = view.selection.getInlineSelection()
val spans = spannable.getSpans(start, end, EnrichedImageSpan::class.java)
val (start, originalEnd) = view.selection.getInlineSelection()

for (s in spans) {
spannable.removeSpan(s)
}

if (start == end) {
if (start == originalEnd) {
spannable.insert(start, "\uFFFC")
end++
} else {
val spans = spannable.getSpans(start, originalEnd, EnrichedImageSpan::class.java)
for (s in spans) {
spannable.removeSpan(s)
}

spannable.replace(start, originalEnd, "\uFFFC")
}

val (imageStart, imageEnd) = spannable.getSafeSpanBoundaries(start, start + 1)
val uri = Uri.fromFile(File(src))
val span = EnrichedImageSpan(view.context, uri, view.htmlStyle)
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
Comment thread
kacperzolkiewski marked this conversation as resolved.
spannable.setSpan(span, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

val span = EnrichedImageSpan(view.context, uri, width.toInt(), height.toInt())
spannable.setSpan(span, imageStart, imageEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}

fun startMention(indicator: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.swmansion.enriched.utils;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.Layout;
Expand All @@ -9,6 +13,7 @@
import android.text.TextUtils;
import android.text.style.AlignmentSpan;
import android.text.style.ParagraphStyle;
import android.util.Log;

import com.swmansion.enriched.spans.EnrichedBlockQuoteSpan;
import com.swmansion.enriched.spans.EnrichedBoldSpan;
Expand Down Expand Up @@ -275,7 +280,16 @@ private static void withinParagraph(StringBuilder out, Spanned text, int start,
if (style[j] instanceof EnrichedImageSpan) {
out.append("<img src=\"");
out.append(((EnrichedImageSpan) style[j]).getSource());
out.append("\">");
out.append("\"");

out.append(" width=\"");
out.append(((EnrichedImageSpan) style[j]).getWidth());
out.append("\"");

out.append(" height=\"");
out.append(((EnrichedImageSpan) style[j]).getHeight());

out.append("\"/>");
// Don't output the placeholder character underlying the image.
i = next;
}
Expand Down Expand Up @@ -454,7 +468,7 @@ private void handleStartTag(String tag, Attributes attributes) {
} else if (tag.equalsIgnoreCase("h3")) {
startHeading(mSpannableStringBuilder, 3);
} else if (tag.equalsIgnoreCase("img")) {
startImg(mSpannableStringBuilder, attributes, mImageGetter, mStyle);
startImg(mSpannableStringBuilder, attributes, mImageGetter);
} else if (tag.equalsIgnoreCase("code")) {
start(mSpannableStringBuilder, new Code());
} else if (tag.equalsIgnoreCase("mention")) {
Expand Down Expand Up @@ -679,20 +693,51 @@ private static void end(Editable text, Class kind, Object repl) {
}
}

private static void startImg(Editable text, Attributes attributes, EnrichedParser.ImageGetter img, HtmlStyle style) {
private static void startImg(Editable text, Attributes attributes, EnrichedParser.ImageGetter img) {
String src = attributes.getValue("", "src");
String width = attributes.getValue("", "width");
String height = attributes.getValue("", "height");

Drawable d = null;
if (img != null) {
d = img.getDrawable(src);
}

if (d == null) {
d = HtmlToSpannedConverter.prepareDrawableForImage(src);
}

if (d == null) {
return;
}

int len = text.length();
text.append("");
text.setSpan(new EnrichedImageSpan(d, src, style), len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new EnrichedImageSpan(d, src, Integer.parseInt(width), Integer.parseInt(height)), len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}

private static BitmapDrawable prepareDrawableForImage(String src) {
String cleanPath = src;
if (cleanPath.startsWith("file://")) {
cleanPath = cleanPath.substring(7);
}

BitmapDrawable drawable = null;

try {
Bitmap bitmap = BitmapFactory.decodeFile(cleanPath);
if (bitmap != null) {
drawable = new BitmapDrawable(Resources.getSystem(), bitmap);
// set bounds so it knows how big it is naturally,
// though EnrichedImageSpan will override this with the HTML width/height later.
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
} catch (Exception e) {
// Failed to load file
Log.e("EnrichedParser", "Failed to load image from path: " + cleanPath, e);
}

return drawable;
}

private static void startA(Editable text, Attributes attributes) {
Expand Down
29 changes: 21 additions & 8 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { type MentionItem, MentionPopup } from './components/MentionPopup';
import { useUserMention } from './useUserMention';
import { useChannelMention } from './useChannelMention';
import { HtmlSection } from './components/HtmlSection';
import { ImageModal } from './components/ImageModal';

type StylesState = OnChangeStateEvent;

Expand Down Expand Up @@ -75,6 +76,7 @@ export default function App() {
const [isChannelPopupOpen, setIsChannelPopupOpen] = useState(false);
const [isUserPopupOpen, setIsUserPopupOpen] = useState(false);
const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
const [isValueModalOpen, setIsValueModalOpen] = useState(false);
const [currentHtml, setCurrentHtml] = useState('');

Expand Down Expand Up @@ -125,6 +127,14 @@ export default function App() {
setIsLinkModalOpen(false);
};

const openImageModal = () => {
setIsImageModalOpen(true);
};

const closeImageModal = () => {
setIsImageModalOpen(false);
};

const openUserMentionPopup = () => {
setIsUserPopupOpen(true);
};
Expand Down Expand Up @@ -197,7 +207,7 @@ export default function App() {
closeValueModal();
};

const selectImage = async () => {
const selectImage = async (width: number, height: number) => {
const response = await launchImageLibrary({
mediaType: 'photo',
selectionLimit: 1,
Expand All @@ -208,9 +218,11 @@ export default function App() {
? response.assets?.[0]?.originalPath
: response.assets?.[0]?.uri;

if (!imageUri) return;
if (imageUri) {
ref.current?.setImage(imageUri, width, height);
}

ref.current?.setImage(imageUri);
closeImageModal();
};

const handleChangeMention = ({ indicator, text }: OnChangeMentionEvent) => {
Expand Down Expand Up @@ -294,7 +306,7 @@ export default function App() {
stylesState={stylesState}
editorRef={ref}
onOpenLinkModal={openLinkModal}
onSelectImage={selectImage}
onSelectImage={openImageModal}
/>
</View>
<View style={styles.buttonStack}>
Expand All @@ -318,6 +330,11 @@ export default function App() {
onSubmit={submitLink}
onClose={closeLinkModal}
/>
<ImageModal
isOpen={isImageModalOpen}
onSubmit={selectImage}
onClose={closeImageModal}
/>
<ValueModal
isOpen={isValueModalOpen}
onSubmit={submitSetValue}
Expand Down Expand Up @@ -383,10 +400,6 @@ const htmlStyle: HtmlStyle = {
textDecorationLine: 'none',
},
},
img: {
width: 50,
height: 50,
},
ol: {
gapWidth: 16,
marginLeft: 24,
Expand Down
Loading
Loading