Custom render: white space handling

pull/178/head
M66B 5 years ago
parent b7268fc303
commit 477719eb95

@ -1767,7 +1767,6 @@ public class HtmlHelper {
if (experiments) { if (experiments) {
// https://developer.android.com/guide/topics/text/spans // https://developer.android.com/guide/topics/text/spans
SpannableStringBuilder ssb = new SpannableStringBuilder(); SpannableStringBuilder ssb = new SpannableStringBuilder();
List<SpanHolder> holders = new ArrayList<>();
NodeTraversor.traverse(new NodeVisitor() { NodeTraversor.traverse(new NodeVisitor() {
@Override @Override
@ -1775,17 +1774,42 @@ public class HtmlHelper {
if (node instanceof Element) { if (node instanceof Element) {
Element element = (Element) node; Element element = (Element) node;
element.attr("start-index", Integer.toString(ssb.length())); element.attr("start-index", Integer.toString(ssb.length()));
switch (element.tagName()) {
case "img": boolean pre = false;
String src = element.attr("src"); Element parent = element.parent();
Drawable d = (imageGetter == null while (parent != null) {
? context.getDrawable(R.drawable.baseline_broken_image_24) if ("pre".equals(parent.tagName())) {
: imageGetter.getDrawable(src)); pre = true;
ssb.append("\uFFFC"); // Object replacement character
holders.add(new SpanHolder(new ImageSpan(d, src), ssb.length() - 1, ssb.length()));
break; break;
} }
parent = parent.parent();
}
if (!pre) {
// https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
List<TextNode> tnodes = getTextNodes(element);
for (int i = 0; i < tnodes.size(); i++) {
TextNode tnode = tnodes.get(i);
String text = tnode.getWholeText();
if (TextUtils.isEmpty(text))
continue;
// Remove whitespace before/after newlines
text = text.replaceAll("\\s+\\r?\\n\\s+", " ");
if (i == 0 || (tnodes.get(i - 1).text().endsWith(" ")))
while (text.startsWith(" "))
text = text.substring(1);
if (i == tnodes.size() - 1)
while (text.endsWith(" "))
text = text.substring(0, text.length() - 1);
tnode.text(text);
}
}
} else if (node instanceof TextNode) { } else if (node instanceof TextNode) {
// https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
TextNode tnode = (TextNode) node; TextNode tnode = (TextNode) node;
ssb.append(tnode.text()); ssb.append(tnode.text());
} }
@ -1799,22 +1823,22 @@ public class HtmlHelper {
switch (element.tagName()) { switch (element.tagName()) {
case "a": case "a":
String href = element.attr("href"); String href = element.attr("href");
holders.add(new SpanHolder(new URLSpan(href), start, ssb.length())); ssb.setSpan(new URLSpan(href), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "body": case "body":
// Do nothing // Do nothing
break; break;
case "big": case "big":
holders.add(new SpanHolder(new RelativeSizeSpan(FONT_LARGE), start, ssb.length())); ssb.setSpan(new RelativeSizeSpan(FONT_LARGE), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "blockquote": case "blockquote":
holders.add(new SpanHolder(new QuoteSpan(), start, ssb.length())); ssb.setSpan(new QuoteSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "br": case "br":
ssb.append("\n"); ssb.append("\n");
break; break;
case "em": case "em":
holders.add(new SpanHolder(new StyleSpan(Typeface.ITALIC), start, ssb.length())); ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "h1": case "h1":
case "h2": case "h2":
@ -1823,23 +1847,33 @@ public class HtmlHelper {
case "h5": case "h5":
case "h6": case "h6":
int level = element.tagName().charAt(1) - '1'; int level = element.tagName().charAt(1) - '1';
holders.add(new SpanHolder(new RelativeSizeSpan(HEADING_SIZES[level]), start, ssb.length())); ssb.setSpan(new RelativeSizeSpan(HEADING_SIZES[level]), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
holders.add(new SpanHolder(new StyleSpan(Typeface.BOLD), start, ssb.length())); ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append("\n");
ssb.insert(start, "\n");
break; break;
case "img": case "img":
String src = element.attr("src");
Drawable d = (imageGetter == null
? context.getDrawable(R.drawable.baseline_broken_image_24)
: imageGetter.getDrawable(src));
ssb.insert(start, "\uFFFC"); // Object replacement character
ssb.setSpan(new ImageSpan(d, src), start, start + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
case "pre":
// Do nothing // Do nothing
break; break;
case "small": case "small":
holders.add(new SpanHolder(new RelativeSizeSpan(FONT_SMALL), start, ssb.length())); ssb.setSpan(new RelativeSizeSpan(FONT_SMALL), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "span": case "span":
// Do nothing // Do nothing
break; break;
case "strong": case "strong":
holders.add(new SpanHolder(new StyleSpan(Typeface.BOLD), start, ssb.length())); ssb.setSpan(new StyleSpan(Typeface.BOLD), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "u": case "u":
holders.add(new SpanHolder(new UnderlineSpan(), start, ssb.length())); ssb.setSpan(new UnderlineSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
default: default:
Log.e("Unknown tag=" + element.tagName()); Log.e("Unknown tag=" + element.tagName());
@ -1855,24 +1889,32 @@ public class HtmlHelper {
switch (key) { switch (key) {
case "color": case "color":
int color = Integer.parseInt(value.substring(1), 16) | 0xFF000000; int color = Integer.parseInt(value.substring(1), 16) | 0xFF000000;
holders.add(new SpanHolder(new ForegroundColorSpan(color), start, ssb.length())); ssb.setSpan(new ForegroundColorSpan(color), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
case "text-decoration": case "text-decoration":
if ("line-through".equals(value)) if ("line-through".equals(value))
holders.add(new SpanHolder(new StrikethroughSpan(), start, ssb.length())); ssb.setSpan(new StrikethroughSpan(), start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break; break;
} }
} }
} }
} }
} }
}, document.body());
Collections.reverse(holders); List<TextNode> getTextNodes(Element element) {
for (SpanHolder holder : holders) List<TextNode> result = new ArrayList<>();
ssb.setSpan(holder.span, holder.start, holder.end, holder.flags);
return ssb; for (Node child : element.childNodes())
if (child instanceof TextNode)
result.add((TextNode) child);
else if (child instanceof Element)
result.addAll(getTextNodes((Element) child));
return result;
}
}, document.body().children());
return reverseSpans(ssb);
} else } else
return fromHtml(document.html(), imageGetter, null); return fromHtml(document.html(), imageGetter, null);
} }
@ -1930,18 +1972,4 @@ public class HtmlHelper {
spanned.getSpanFlags(spans[i])); spanned.getSpanFlags(spans[i]));
return reverse; return reverse;
} }
private static class SpanHolder {
Object span;
int start;
int end;
int flags;
SpanHolder(Object span, int start, int end) {
this.span = span;
this.start = start;
this.end = end;
this.flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
}
}
} }

Loading…
Cancel
Save