Image sizes specified in HTML

This commit is contained in:
Dimitry Ivanov 2017-11-11 15:43:41 +03:00
parent 35dcf079b2
commit 3332a722d4
8 changed files with 354 additions and 245 deletions

View File

@ -3,6 +3,8 @@ package ru.noties.markwon;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import ru.noties.markwon.renderer.html.ImageSizeResolver;
import ru.noties.markwon.renderer.html.ImageSizeResolverDef;
import ru.noties.markwon.renderer.html.SpannableHtmlParser; import ru.noties.markwon.renderer.html.SpannableHtmlParser;
import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.LinkSpan; import ru.noties.markwon.spans.LinkSpan;
@ -12,10 +14,12 @@ import ru.noties.markwon.spans.SpannableTheme;
public class SpannableConfiguration { public class SpannableConfiguration {
// creates default configuration // creates default configuration
@NonNull
public static SpannableConfiguration create(@NonNull Context context) { public static SpannableConfiguration create(@NonNull Context context) {
return new Builder(context).build(); return new Builder(context).build();
} }
@NonNull
public static Builder builder(@NonNull Context context) { public static Builder builder(@NonNull Context context) {
return new Builder(context); return new Builder(context);
} }
@ -27,7 +31,7 @@ public class SpannableConfiguration {
private final UrlProcessor urlProcessor; private final UrlProcessor urlProcessor;
private final SpannableHtmlParser htmlParser; private final SpannableHtmlParser htmlParser;
private SpannableConfiguration(Builder builder) { private SpannableConfiguration(@NonNull Builder builder) {
this.theme = builder.theme; this.theme = builder.theme;
this.asyncDrawableLoader = builder.asyncDrawableLoader; this.asyncDrawableLoader = builder.asyncDrawableLoader;
this.syntaxHighlight = builder.syntaxHighlight; this.syntaxHighlight = builder.syntaxHighlight;
@ -36,26 +40,32 @@ public class SpannableConfiguration {
this.htmlParser = builder.htmlParser; this.htmlParser = builder.htmlParser;
} }
@NonNull
public SpannableTheme theme() { public SpannableTheme theme() {
return theme; return theme;
} }
@NonNull
public AsyncDrawable.Loader asyncDrawableLoader() { public AsyncDrawable.Loader asyncDrawableLoader() {
return asyncDrawableLoader; return asyncDrawableLoader;
} }
@NonNull
public SyntaxHighlight syntaxHighlight() { public SyntaxHighlight syntaxHighlight() {
return syntaxHighlight; return syntaxHighlight;
} }
@NonNull
public LinkSpan.Resolver linkResolver() { public LinkSpan.Resolver linkResolver() {
return linkResolver; return linkResolver;
} }
@NonNull
public UrlProcessor urlProcessor() { public UrlProcessor urlProcessor() {
return urlProcessor; return urlProcessor;
} }
@NonNull
public SpannableHtmlParser htmlParser() { public SpannableHtmlParser htmlParser() {
return htmlParser; return htmlParser;
} }
@ -69,60 +79,89 @@ public class SpannableConfiguration {
private LinkSpan.Resolver linkResolver; private LinkSpan.Resolver linkResolver;
private UrlProcessor urlProcessor; private UrlProcessor urlProcessor;
private SpannableHtmlParser htmlParser; private SpannableHtmlParser htmlParser;
private ImageSizeResolver imageSizeResolver;
Builder(Context context) { Builder(@NonNull Context context) {
this.context = context; this.context = context;
} }
public Builder theme(SpannableTheme theme) { @NonNull
public Builder theme(@NonNull SpannableTheme theme) {
this.theme = theme; this.theme = theme;
return this; return this;
} }
public Builder asyncDrawableLoader(AsyncDrawable.Loader asyncDrawableLoader) { @NonNull
public Builder asyncDrawableLoader(@NonNull AsyncDrawable.Loader asyncDrawableLoader) {
this.asyncDrawableLoader = asyncDrawableLoader; this.asyncDrawableLoader = asyncDrawableLoader;
return this; return this;
} }
public Builder syntaxHighlight(SyntaxHighlight syntaxHighlight) { @NonNull
public Builder syntaxHighlight(@NonNull SyntaxHighlight syntaxHighlight) {
this.syntaxHighlight = syntaxHighlight; this.syntaxHighlight = syntaxHighlight;
return this; return this;
} }
public Builder linkResolver(LinkSpan.Resolver linkResolver) { @NonNull
public Builder linkResolver(@NonNull LinkSpan.Resolver linkResolver) {
this.linkResolver = linkResolver; this.linkResolver = linkResolver;
return this; return this;
} }
public Builder urlProcessor(UrlProcessor urlProcessor) { @NonNull
public Builder urlProcessor(@NonNull UrlProcessor urlProcessor) {
this.urlProcessor = urlProcessor; this.urlProcessor = urlProcessor;
return this; return this;
} }
public Builder htmlParser(SpannableHtmlParser htmlParser) { @NonNull
public Builder htmlParser(@NonNull SpannableHtmlParser htmlParser) {
this.htmlParser = htmlParser; this.htmlParser = htmlParser;
return this; return this;
} }
/**
* @since 1.0.1
*/
@NonNull
public Builder imageSizeResolver(@NonNull ImageSizeResolver imageSizeResolver) {
this.imageSizeResolver = imageSizeResolver;
return this;
}
@NonNull
public SpannableConfiguration build() { public SpannableConfiguration build() {
if (theme == null) { if (theme == null) {
theme = SpannableTheme.create(context); theme = SpannableTheme.create(context);
} }
if (asyncDrawableLoader == null) { if (asyncDrawableLoader == null) {
asyncDrawableLoader = new AsyncDrawableLoaderNoOp(); asyncDrawableLoader = new AsyncDrawableLoaderNoOp();
} }
if (syntaxHighlight == null) { if (syntaxHighlight == null) {
syntaxHighlight = new SyntaxHighlightNoOp(); syntaxHighlight = new SyntaxHighlightNoOp();
} }
if (linkResolver == null) { if (linkResolver == null) {
linkResolver = new LinkResolverDef(); linkResolver = new LinkResolverDef();
} }
if (urlProcessor == null) { if (urlProcessor == null) {
urlProcessor = new UrlProcessorNoOp(); urlProcessor = new UrlProcessorNoOp();
} }
if (htmlParser == null) { if (htmlParser == null) {
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver);
if (imageSizeResolver == null) {
imageSizeResolver = new ImageSizeResolverDef();
} }
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver, imageSizeResolver);
}
return new SpannableConfiguration(this); return new SpannableConfiguration(this);
} }
} }

View File

@ -6,9 +6,9 @@ import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ru.noties.markwon.UrlProcessor; import ru.noties.markwon.UrlProcessor;
import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawable;
@ -17,20 +17,21 @@ import ru.noties.markwon.spans.SpannableTheme;
class ImageProviderImpl implements SpannableHtmlParser.ImageProvider { class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
private static final Pattern STYLE_WIDTH = Pattern.compile(".*width:\\s*(\\d+)(%|em|px)*.*");
private static final Pattern STYLE_HEIGHT = Pattern.compile(".*height:\\s*(\\d+)(%|em|px)*.*");
private final SpannableTheme theme; private final SpannableTheme theme;
private final AsyncDrawable.Loader loader; private final AsyncDrawable.Loader loader;
private final UrlProcessor urlProcessor; private final UrlProcessor urlProcessor;
private final ImageSizeResolver imageSizeResolver;
ImageProviderImpl( ImageProviderImpl(
@NonNull SpannableTheme theme, @NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader, @NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor) { @NonNull UrlProcessor urlProcessor,
@NonNull ImageSizeResolver imageSizeResolver
) {
this.theme = theme; this.theme = theme;
this.loader = loader; this.loader = loader;
this.urlProcessor = urlProcessor; this.urlProcessor = urlProcessor;
this.imageSizeResolver = imageSizeResolver;
} }
@Override @Override
@ -53,7 +54,7 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
replacement = "\uFFFC"; replacement = "\uFFFC";
} }
final AsyncDrawable drawable = new AsyncDrawable(destination, loader, parseImageSize(attributes)); final AsyncDrawable drawable = new AsyncDrawable(destination, loader, imageSizeResolver, parseImageSize(attributes));
final AsyncDrawableSpan span = new AsyncDrawableSpan(theme, drawable); final AsyncDrawableSpan span = new AsyncDrawableSpan(theme, drawable);
final SpannableString string = new SpannableString(replacement); final SpannableString string = new SpannableString(replacement);
@ -70,145 +71,134 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
@Nullable @Nullable
private static ImageSize parseImageSize(@NonNull Map<String, String> attributes) { private static ImageSize parseImageSize(@NonNull Map<String, String> attributes) {
return null; final ImageSize imageSize;
// final String width = attributes.get("width"); final StyleProvider styleProvider = new StyleProvider(attributes.get("style"));
// final String height = attributes.get("height");
// final ImageSize.Dimension width = parseDimension(extractDimension("width", attributes, styleProvider));
// final ImageSize imageSize; final ImageSize.Dimension height = parseDimension(extractDimension("height", attributes, styleProvider));
//
// final String width = extractWidth(attributes); if (width == null
// final String height = extractHeight(attributes); && height == null) {
// imageSize = null;
// if (TextUtils.isEmpty(width) } else {
// && TextUtils.isEmpty(height)) { imageSize = new ImageSize(width, height);
// imageSize = null;
// } else {
// if (isRelative(width)) {
// // check if height is NOT relative, if it is -> ignore
// final int h = isRelative(height)
// ? 0
// : parseInt(height);
// imageSize = new ImageSize(, parseInt(width), h);
// } else {
// imageSize = new ImageSize(false, parseInt(width), parseInt(height));
// }
// }
//
// return imageSize;
} }
// @Nullable return imageSize;
// private static ImageSize.Dimension parseWidth(@NonNull Map<String, String> attributes) {
//
// // so, we can have raw value specified via direct attribute
//
// final ImageSize.Dimension dimension;
//
// final String width = attributes.get("width");
// if (!TextUtils.isEmpty(width)) {
// final Matcher matcher =
// }
// }
@Nullable
private static String extractWidth(@NonNull Map<String, String> attributes) {
// let's check style first
String width = attributes.get("width");
if (width == null) {
final String style = attributes.get("style");
if (!TextUtils.isEmpty(style)) {
final Matcher matcher = STYLE_WIDTH.matcher(style);
if (matcher.matches()) {
width = matcher.group(1);
}
}
}
return width;
} }
@Nullable @Nullable
private static String extractHeight(@NonNull Map<String, String> attributes) { private static String extractDimension(@NonNull String name, @NonNull Map<String, String> attributes, @NonNull StyleProvider styleProvider) {
String height = attributes.get("height"); final String out;
if (height == null) { final String inline = attributes.get(name);
final String style = attributes.get("style"); if (!TextUtils.isEmpty(inline)) {
if (!TextUtils.isEmpty(style)) { out = inline;
final Matcher matcher = STYLE_HEIGHT.matcher(style); } else {
if (matcher.matches()) { out = extractDimensionFromStyle(name, styleProvider);
height = matcher.group(1);
}
}
} }
return height; return out;
} }
@Nullable @Nullable
private static ImageSize.Dimension extractFromStyle(@Nullable String style, @NonNull Pattern pattern) { private static String extractDimensionFromStyle(@NonNull String name, @NonNull StyleProvider styleProvider) {
return styleProvider.attributes().get(name);
}
@Nullable
private static ImageSize.Dimension parseDimension(@Nullable String raw) {
// a set of digits, then dimension unit (allow floating)
final ImageSize.Dimension dimension; final ImageSize.Dimension dimension;
if (style == null) {
final int length = raw != null
? raw.length()
: 0;
if (length == 0) {
dimension = null; dimension = null;
} else { } else {
final Matcher matcher = pattern.matcher(style);
if (matcher.matches()) { // first digit to find -> unit is finished (can be null)
dimension = new ImageSize.Dimension(
parseUnit(matcher.group(2)), int index = -1;
parseInt(matcher.group(1))
); for (int i = length - 1; i >= 0; i--) {
} else { if (Character.isDigit(raw.charAt(i))) {
index = i;
break;
}
}
// no digits -> no dimension
if (index == -1) {
dimension = null; dimension = null;
} else {
final String value;
final String unit;
// no unit is specified
if (index == length - 1) {
value = raw;
unit = null;
} else {
value = raw.substring(0, index + 1);
unit = raw.substring(index + 1);
}
ImageSize.Dimension inner;
try {
final float floatValue = Float.parseFloat(value);
inner = new ImageSize.Dimension(floatValue, unit);
} catch (NumberFormatException e) {
inner = null;
}
dimension = inner;
} }
} }
return dimension; return dimension;
} }
private static class StyleProvider {
private final String style;
private Map<String, String> attributes;
StyleProvider(@Nullable String style) {
this.style = style;
}
@NonNull @NonNull
private static ImageSize.Unit parseUnit(@Nullable String s) { Map<String, String> attributes() {
final Map<String, String> out;
final ImageSize.Unit unit; if (attributes != null) {
out = attributes;
if (TextUtils.isEmpty(s)) {
unit = ImageSize.Unit.PIXELS;
} else { } else {
if (TextUtils.isEmpty(style)) {
final int length = s.length(); out = attributes = Collections.emptyMap();
if (length == 1
&& '%' == s.charAt(length - 1)) {
unit = ImageSize.Unit.PERCENT;
} else if (length == 2
&& 'e' == s.charAt(length - 2)
&& 'm' == s.charAt(length - 1)) {
unit = ImageSize.Unit.FONT_SIZE;
} else { } else {
unit = ImageSize.Unit.PIXELS; final String[] split = style.split(";");
} final Map<String, String> map = new HashMap<>(split.length);
} String[] parts;
for (String s : split) {
return unit;
}
private static boolean isRelative(@Nullable String attr) {
return attr != null && attr.charAt(attr.length() - 1) == '%';
}
private static int parseInt(@Nullable String s) {
int result = 0;
if (!TextUtils.isEmpty(s)) { if (!TextUtils.isEmpty(s)) {
try { parts = s.split(":");
result = Integer.parseInt(s.replaceAll("[^\\d]", "")); if (parts.length == 2) {
} catch (NumberFormatException e) { map.put(parts[0].trim(), parts[1].trim());
result = 0;
} }
} }
return result; }
out = attributes = map;
}
}
return out;
}
} }
} }

View File

@ -1,72 +1,37 @@
package ru.noties.markwon.renderer.html; package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
/** /**
* @since 1.0.1 * @since 1.0.1
*/ */
@SuppressWarnings("WeakerAccess")
public class ImageSize { public class ImageSize {
public enum Unit {
PERCENT, FONT_SIZE, PIXELS
}
public static class Dimension { public static class Dimension {
private final Unit unit; public final float value;
private final int value; public final String unit;
public Dimension(@NonNull Unit unit, int value) { public Dimension(float value, @Nullable String unit) {
this.unit = unit;
this.value = value; this.value = value;
} this.unit = unit;
@NonNull
public Unit unit() {
return unit;
}
public int value() {
return value;
} }
@Override @Override
public String toString() { public String toString() {
return "Dimension{" + return "Dimension{" +
"unit=" + unit + "value=" + value +
", value=" + value + ", unit='" + unit + '\'' +
'}'; '}';
} }
} }
// width can be relative (in percent) public final Dimension width;
// height CANNOT be relative (endless loop) public final Dimension height;
// both can be absolute
private final Dimension width;
private final Dimension height;
public ImageSize(@Nullable Dimension width, @Nullable Dimension height) { public ImageSize(@Nullable Dimension width, @Nullable Dimension height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
} }
@Nullable
public Dimension width() {
return width;
}
@Nullable
public Dimension height() {
return height;
}
@Override
public String toString() {
return "ImageSize{" +
"width=" + width +
", height=" + height +
'}';
}
} }

View File

@ -0,0 +1,29 @@
package ru.noties.markwon.renderer.html;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* @since 1.0.1
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class ImageSizeResolver {
/**
* We do not expose canvas height deliberately. As we cannot rely on this value very much
*
* @param imageSize {@link ImageSize} parsed from HTML
* @param imageBounds original image bounds
* @param canvasWidth width of the canvas
* @param textSize current font size
* @return resolved image bounds
*/
@NonNull
public abstract Rect resolveImageSize(
@Nullable ImageSize imageSize,
@NonNull Rect imageBounds,
int canvasWidth,
float textSize
);
}

View File

@ -0,0 +1,85 @@
package ru.noties.markwon.renderer.html;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* @since 1.0.1
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class ImageSizeResolverDef extends ImageSizeResolver {
// we track these two, others are considered to be pixels
protected static final String UNIT_PERCENT = "%";
protected static final String UNIT_EM = "em";
@NonNull
@Override
public Rect resolveImageSize(
@Nullable ImageSize imageSize,
@NonNull Rect imageBounds,
int canvasWidth,
float textSize
) {
if (imageSize == null) {
return imageBounds;
}
final Rect rect;
final ImageSize.Dimension width = imageSize.width;
final ImageSize.Dimension height = imageSize.height;
final int imageWidth = imageBounds.width();
final int imageHeight = imageBounds.height();
final float ratio = (float) imageWidth / imageHeight;
if (width != null) {
final int w;
final int h;
if (UNIT_PERCENT.equals(width.unit)) {
w = (int) (canvasWidth * (width.value / 100.F) + .5F);
} else {
w = resolveAbsolute(width, imageWidth, textSize);
}
if (height == null
|| UNIT_PERCENT.equals(height.unit)) {
h = (int) (w / ratio + .5F);
} else {
h = resolveAbsolute(height, imageHeight, textSize);
}
rect = new Rect(0, 0, w, h);
} else if (height != null) {
if (!UNIT_PERCENT.equals(height.unit)) {
final int h = resolveAbsolute(height, imageHeight, textSize);
final int w = (int) (h * ratio + .5F);
rect = new Rect(0, 0, w, h);
} else {
rect = imageBounds;
}
} else {
rect = imageBounds;
}
return rect;
}
protected int resolveAbsolute(@NonNull ImageSize.Dimension dimension, int original, float textSize) {
final int out;
if (UNIT_EM.equals(dimension.unit)) {
out = (int) (dimension.value * textSize + .5F);
} else {
out = original;
}
return out;
}
}

View File

@ -21,37 +21,74 @@ import ru.noties.markwon.spans.SpannableTheme;
public class SpannableHtmlParser { public class SpannableHtmlParser {
// creates default parser // creates default parser
@NonNull
public static SpannableHtmlParser create( public static SpannableHtmlParser create(
@NonNull SpannableTheme theme, @NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader @NonNull AsyncDrawable.Loader loader
) { ) {
return builderWithDefaults(theme, loader, null, null) return builderWithDefaults(theme, loader, null, null, null)
.build(); .build();
} }
/**
* @since 1.0.1
*/
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull ImageSizeResolver imageSizeResolver
) {
return builderWithDefaults(theme, loader, null, null, imageSizeResolver)
.build();
}
@NonNull
public static SpannableHtmlParser create( public static SpannableHtmlParser create(
@NonNull SpannableTheme theme, @NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader, @NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor, @NonNull UrlProcessor urlProcessor,
@NonNull LinkSpan.Resolver resolver @NonNull LinkSpan.Resolver resolver
) { ) {
return builderWithDefaults(theme, loader, urlProcessor, resolver) return builderWithDefaults(theme, loader, urlProcessor, resolver, null)
.build(); .build();
} }
/**
* @since 1.0.1
*/
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor,
@NonNull LinkSpan.Resolver resolver,
@NonNull ImageSizeResolver imageSizeResolver
) {
return builderWithDefaults(theme, loader, urlProcessor, resolver, imageSizeResolver)
.build();
}
@NonNull
public static Builder builder() { public static Builder builder() {
return new Builder(); return new Builder();
} }
@NonNull
public static Builder builderWithDefaults(@NonNull SpannableTheme theme) { public static Builder builderWithDefaults(@NonNull SpannableTheme theme) {
return builderWithDefaults(theme, null, null, null); return builderWithDefaults(theme, null, null, null, null);
} }
/**
* Updated in 1.0.1: added imageSizeResolverArgument
*/
@NonNull
public static Builder builderWithDefaults( public static Builder builderWithDefaults(
@NonNull SpannableTheme theme, @NonNull SpannableTheme theme,
@Nullable AsyncDrawable.Loader asyncDrawableLoader, @Nullable AsyncDrawable.Loader asyncDrawableLoader,
@Nullable UrlProcessor urlProcessor, @Nullable UrlProcessor urlProcessor,
@Nullable LinkSpan.Resolver resolver @Nullable LinkSpan.Resolver resolver,
@Nullable ImageSizeResolver imageSizeResolver
) { ) {
if (urlProcessor == null) { if (urlProcessor == null) {
@ -68,7 +105,12 @@ public class SpannableHtmlParser {
final ImageProvider imageProvider; final ImageProvider imageProvider;
if (asyncDrawableLoader != null) { if (asyncDrawableLoader != null) {
imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor);
if (imageSizeResolver == null) {
imageSizeResolver = new ImageSizeResolverDef();
}
imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor, imageSizeResolver);
} else { } else {
imageProvider = null; imageProvider = null;
} }
@ -163,21 +205,25 @@ public class SpannableHtmlParser {
private ImageProvider imageProvider; private ImageProvider imageProvider;
private HtmlParser parser; private HtmlParser parser;
@NonNull
Builder simpleTag(@NonNull String tag, @NonNull SpanProvider provider) { Builder simpleTag(@NonNull String tag, @NonNull SpanProvider provider) {
simpleTags.put(tag, provider); simpleTags.put(tag, provider);
return this; return this;
} }
public Builder imageProvider(ImageProvider imageProvider) { @NonNull
public Builder imageProvider(@Nullable ImageProvider imageProvider) {
this.imageProvider = imageProvider; this.imageProvider = imageProvider;
return this; return this;
} }
@NonNull
public Builder parser(@NonNull HtmlParser parser) { public Builder parser(@NonNull HtmlParser parser) {
this.parser = parser; this.parser = parser;
return this; return this;
} }
@NonNull
public SpannableHtmlParser build() { public SpannableHtmlParser build() {
if (parser == null) { if (parser == null) {
parser = DefaultHtmlParser.create(); parser = DefaultHtmlParser.create();

View File

@ -11,6 +11,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import ru.noties.markwon.renderer.html.ImageSize; import ru.noties.markwon.renderer.html.ImageSize;
import ru.noties.markwon.renderer.html.ImageSizeResolver;
public class AsyncDrawable extends Drawable { public class AsyncDrawable extends Drawable {
@ -24,22 +25,30 @@ public class AsyncDrawable extends Drawable {
private final String destination; private final String destination;
private final Loader loader; private final Loader loader;
private final ImageSize imageSize; private final ImageSize imageSize;
private final ImageSizeResolver imageSizeResolver;
private Drawable result; private Drawable result;
private Callback callback; private Callback callback;
private int canvasWidth; private int canvasWidth;
private float textSize;
public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) { public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) {
this(destination, loader, null); this(destination, loader, null, null);
} }
/** /**
* @since 1.0.1 * @since 1.0.1
*/ */
public AsyncDrawable(@NonNull String destination, @NonNull Loader loader, @Nullable ImageSize imageSize) { public AsyncDrawable(
@NonNull String destination,
@NonNull Loader loader,
@Nullable ImageSizeResolver imageSizeResolver,
@Nullable ImageSize imageSize
) {
this.destination = destination; this.destination = destination;
this.loader = loader; this.loader = loader;
this.imageSizeResolver = imageSizeResolver;
this.imageSize = imageSize; this.imageSize = imageSize;
} }
@ -103,8 +112,9 @@ public class AsyncDrawable extends Drawable {
* @since 1.0.1 * @since 1.0.1
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public void initWithCanvasWidth(int width) { public void initWithKnownDimensions(int width, float textSize) {
this.canvasWidth = width; this.canvasWidth = width;
this.textSize = textSize;
} }
@Override @Override
@ -162,70 +172,13 @@ public class AsyncDrawable extends Drawable {
*/ */
@NonNull @NonNull
private Rect resolveBounds() { private Rect resolveBounds() {
final Rect rect;
return result.getBounds(); if (imageSizeResolver == null
|| imageSize == null) {
// final Rect rect; rect = result.getBounds();
// } else {
// if (canvasWidth == 0 rect = imageSizeResolver.resolveImageSize(imageSize, result.getBounds(), canvasWidth, textSize);
// || imageSize == null) { }
// return rect;
// rect = result.getBounds();
//
// } else {
//
// final Rect bounds = result.getBounds();
// final float ratio = (float) bounds.width() / bounds.height();
//
// if (imageSize.widthIsRelative()) {
//
// final int w = (int) (canvasWidth * ((float) imageSize.width() / 100.F) + .5F);
// final int h;
//
// // we still should allow absolute height
// if (imageSize.height() > 0) {
// h = imageSize.height();
// } else {
// h = (int) (w / ratio);
// }
//
// rect = new Rect(0, 0, w, h);
//
// } else {
//
// // if width is specified, but height not -> calculate by ratio (and vice versa)
// // else
//
// final int w;
// final int h;
//
// final int width = imageSize.width();
// final int height = imageSize.height();
//
// if (width > 0
// && height > 0) {
// w = width;
// h = height;
// } else if (width > 0) {
// w = width;
// h = (int) (w / ratio + .5F);
// } else if (height > 0) {
// h = height;
// w = (int) (h * ratio + .5F);
// } else {
// w = 0;
// h = 0;
// }
//
// if (w == 0
// || h == 0) {
// rect = bounds;
// } else {
// rect = new Rect(0, 0, w, h);
// }
// }
// }
//
// return rect;
} }
} }

View File

@ -112,7 +112,9 @@ public class AsyncDrawableSpan extends ReplacementSpan {
int bottom, int bottom,
@NonNull Paint paint) { @NonNull Paint paint) {
drawable.initWithCanvasWidth(canvas.getWidth()); // we will pass this value, so when image is obtained drawable will have dimensions
// to resolve image size
drawable.initWithKnownDimensions(canvas.getWidth(), paint.getTextSize());
this.lastKnownDrawX = (int) (x + .5F); this.lastKnownDrawX = (int) (x + .5F);
this.lastKnownDrawY = y; this.lastKnownDrawY = y;