diff --git a/README.md b/README.md
index 1e33a9f4..5fe29ccf 100644
--- a/README.md
+++ b/README.md
@@ -50,8 +50,10 @@ compile 'ru.noties:markwon-view:1.0.0' // optional
Taken with default configuration (except for image loading):
-
-
+
+
+
+
By default configuration uses TextView textColor for styling, so changing textColor changes style
diff --git a/app/build.gradle b/app/build.gradle
index fab6b399..bc2bca96 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,7 +19,7 @@ android {
buildTypes {
debug {
- minifyEnabled true
+ minifyEnabled false
proguardFile 'proguard.pro'
}
}
diff --git a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
index 21b0ae7a..f276dfe2 100644
--- a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
+++ b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java
@@ -3,6 +3,7 @@ package ru.noties.markwon;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
+import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -11,6 +12,7 @@ import java.util.concurrent.Future;
import javax.inject.Inject;
+import ru.noties.debug.Debug;
import ru.noties.markwon.spans.AsyncDrawable;
@ActivityScope
@@ -57,8 +59,14 @@ public class MarkdownRenderer {
.urlProcessor(urlProcessor)
.build();
+ final long start = SystemClock.uptimeMillis();
+
final CharSequence text = Markwon.markdown(configuration, markdown);
+ final long end = SystemClock.uptimeMillis();
+
+ Debug.i("markdown rendered: %d ms", end - start);
+
if (!isCancelled()) {
handler.post(new Runnable() {
@Override
diff --git a/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java b/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java
index 3e45caee..d9f589cc 100644
--- a/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java
+++ b/library/src/main/java/ru/noties/markwon/renderer/html/ImageProviderImpl.java
@@ -1,11 +1,14 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import ru.noties.markwon.UrlProcessor;
import ru.noties.markwon.spans.AsyncDrawable;
@@ -14,6 +17,9 @@ import ru.noties.markwon.spans.SpannableTheme;
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 AsyncDrawable.Loader loader;
private final UrlProcessor urlProcessor;
@@ -47,7 +53,7 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
replacement = "\uFFFC";
}
- final AsyncDrawable drawable = new AsyncDrawable(destination, loader);
+ final AsyncDrawable drawable = new AsyncDrawable(destination, loader, parseImageSize(attributes));
final AsyncDrawableSpan span = new AsyncDrawableSpan(theme, drawable);
final SpannableString string = new SpannableString(replacement);
@@ -60,4 +66,149 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
return spanned;
}
+
+ @Nullable
+ private static ImageSize parseImageSize(@NonNull Map attributes) {
+
+ return null;
+
+// final String width = attributes.get("width");
+// final String height = attributes.get("height");
+//
+// final ImageSize imageSize;
+//
+// final String width = extractWidth(attributes);
+// final String height = extractHeight(attributes);
+//
+// if (TextUtils.isEmpty(width)
+// && TextUtils.isEmpty(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
+// private static ImageSize.Dimension parseWidth(@NonNull Map 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 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
+ private static String extractHeight(@NonNull Map attributes) {
+
+ String height = attributes.get("height");
+
+ if (height == null) {
+ final String style = attributes.get("style");
+ if (!TextUtils.isEmpty(style)) {
+ final Matcher matcher = STYLE_HEIGHT.matcher(style);
+ if (matcher.matches()) {
+ height = matcher.group(1);
+ }
+ }
+ }
+
+ return height;
+ }
+
+ @Nullable
+ private static ImageSize.Dimension extractFromStyle(@Nullable String style, @NonNull Pattern pattern) {
+ final ImageSize.Dimension dimension;
+ if (style == null) {
+ dimension = null;
+ } else {
+ final Matcher matcher = pattern.matcher(style);
+ if (matcher.matches()) {
+ dimension = new ImageSize.Dimension(
+ parseUnit(matcher.group(2)),
+ parseInt(matcher.group(1))
+ );
+ } else {
+ dimension = null;
+ }
+ }
+ return dimension;
+ }
+
+ @NonNull
+ private static ImageSize.Unit parseUnit(@Nullable String s) {
+
+ final ImageSize.Unit unit;
+
+ if (TextUtils.isEmpty(s)) {
+
+ unit = ImageSize.Unit.PIXELS;
+
+ } else {
+
+ final int length = s.length();
+
+ 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 {
+ unit = ImageSize.Unit.PIXELS;
+ }
+ }
+
+ 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)) {
+ try {
+ result = Integer.parseInt(s.replaceAll("[^\\d]", ""));
+ } catch (NumberFormatException e) {
+ result = 0;
+ }
+ }
+ return result;
+ }
}
diff --git a/library/src/main/java/ru/noties/markwon/renderer/html/ImageSize.java b/library/src/main/java/ru/noties/markwon/renderer/html/ImageSize.java
new file mode 100644
index 00000000..0f916716
--- /dev/null
+++ b/library/src/main/java/ru/noties/markwon/renderer/html/ImageSize.java
@@ -0,0 +1,72 @@
+package ru.noties.markwon.renderer.html;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * @since 1.0.1
+ */
+public class ImageSize {
+
+ public enum Unit {
+ PERCENT, FONT_SIZE, PIXELS
+ }
+
+ public static class Dimension {
+
+ private final Unit unit;
+ private final int value;
+
+ public Dimension(@NonNull Unit unit, int value) {
+ this.unit = unit;
+ this.value = value;
+ }
+
+ @NonNull
+ public Unit unit() {
+ return unit;
+ }
+
+ public int value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "Dimension{" +
+ "unit=" + unit +
+ ", value=" + value +
+ '}';
+ }
+ }
+
+ // width can be relative (in percent)
+ // height CANNOT be relative (endless loop)
+ // both can be absolute
+
+ private final Dimension width;
+ private final Dimension height;
+
+ public ImageSize(@Nullable Dimension width, @Nullable Dimension height) {
+ this.width = width;
+ 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 +
+ '}';
+ }
+}
diff --git a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
index b466e194..4471e699 100644
--- a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
+++ b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java
@@ -3,15 +3,19 @@ package ru.noties.markwon.spans;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import ru.noties.markwon.renderer.html.ImageSize;
+
public class AsyncDrawable extends Drawable {
public interface Loader {
+
void load(@NonNull String destination, @NonNull AsyncDrawable drawable);
void cancel(@NonNull String destination);
@@ -19,13 +23,24 @@ public class AsyncDrawable extends Drawable {
private final String destination;
private final Loader loader;
+ private final ImageSize imageSize;
private Drawable result;
private Callback callback;
+ private int canvasWidth;
+
public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) {
+ this(destination, loader, null);
+ }
+
+ /**
+ * @since 1.0.1
+ */
+ public AsyncDrawable(@NonNull String destination, @NonNull Loader loader, @Nullable ImageSize imageSize) {
this.destination = destination;
this.loader = loader;
+ this.imageSize = imageSize;
}
public String getDestination() {
@@ -77,13 +92,21 @@ public class AsyncDrawable extends Drawable {
this.result = result;
this.result.setCallback(callback);
- // should we copy the data here? like bounds etc?
- // if we are async and we load some image from some source
- // thr bounds might change... so we are better off copy `result` bounds to this instance
- setBounds(result.getBounds());
+ final Rect bounds = resolveBounds();
+ result.setBounds(bounds);
+ setBounds(bounds);
+
invalidateSelf();
}
+ /**
+ * @since 1.0.1
+ */
+ @SuppressWarnings("WeakerAccess")
+ public void initWithCanvasWidth(int width) {
+ this.canvasWidth = width;
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
if (hasResult()) {
@@ -133,4 +156,76 @@ public class AsyncDrawable extends Drawable {
}
return out;
}
+
+ /**
+ * @since 1.0.1
+ */
+ @NonNull
+ private Rect resolveBounds() {
+
+ return result.getBounds();
+
+// final Rect rect;
+//
+// if (canvasWidth == 0
+// || imageSize == null) {
+//
+// 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;
+ }
}
diff --git a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
index 25d01b00..216203d1 100644
--- a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
+++ b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java
@@ -112,6 +112,8 @@ public class AsyncDrawableSpan extends ReplacementSpan {
int bottom,
@NonNull Paint paint) {
+ drawable.initWithCanvasWidth(canvas.getWidth());
+
this.lastKnownDrawX = (int) (x + .5F);
this.lastKnownDrawY = y;