mirror of
https://github.com/k0shk0sh/FastHub.git
synced 2025-12-08 19:05:54 +00:00
Migrate Markdown Library.
Previously using a customized version of zzhoujay/Markdown, FastHub now uses atlassian/commonmark-java, for better performance, and higher control.
This commit is contained in:
parent
318c784d5e
commit
870bef80db
@ -3,17 +3,12 @@ package com.fastaccess.provider.markdown;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.annimon.stream.IntStream;
|
||||
import com.fastaccess.helper.InputHelper;
|
||||
import com.fastaccess.helper.ViewHelper;
|
||||
import com.fastaccess.provider.timeline.handler.DrawableGetter;
|
||||
import com.zzhoujay.markdown.MarkDown;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
package com.zzhoujay.markdown;
|
||||
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zzhoujay.markdown.parser.StyleBuilderImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-25.
|
||||
* Markdown解析器
|
||||
*/
|
||||
public class MarkDown {
|
||||
|
||||
public static Spanned fromMarkdown(String source, Html.ImageGetter imageGetter, TextView textView) {
|
||||
MarkDownParser parser = new MarkDownParser(source, new StyleBuilderImpl(textView, imageGetter));
|
||||
try {
|
||||
return parser.parse();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Spanned fromMarkdown(String source, TextView textView, int codeColor, int headerColor, Html.ImageGetter imageGetter) {
|
||||
MarkDownParser parser = new MarkDownParser(source, new StyleBuilderImpl(textView, codeColor, headerColor, imageGetter));
|
||||
try {
|
||||
return parser.parse();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,309 +0,0 @@
|
||||
package com.zzhoujay.markdown;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
|
||||
import com.zzhoujay.markdown.parser.Line;
|
||||
import com.zzhoujay.markdown.parser.LineQueue;
|
||||
import com.zzhoujay.markdown.parser.QueueConsumer;
|
||||
import com.zzhoujay.markdown.parser.StyleBuilder;
|
||||
import com.zzhoujay.markdown.parser.Tag;
|
||||
import com.zzhoujay.markdown.parser.TagHandler;
|
||||
import com.zzhoujay.markdown.parser.TagHandlerImpl;
|
||||
import com.zzhoujay.markdown.style.ScaleHeightSpan;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-25.
|
||||
*/
|
||||
class MarkDownParser {
|
||||
|
||||
|
||||
private BufferedReader reader;
|
||||
private TagHandler tagHandler;
|
||||
|
||||
MarkDownParser(BufferedReader reader, StyleBuilder styleBuilder) {
|
||||
this.reader = reader;
|
||||
tagHandler = new TagHandlerImpl(styleBuilder);
|
||||
}
|
||||
|
||||
MarkDownParser(InputStream inputStream, StyleBuilder styleBuilder) {
|
||||
this(new BufferedReader(new InputStreamReader(inputStream)), styleBuilder);
|
||||
}
|
||||
|
||||
MarkDownParser(String text, StyleBuilder styleBuilder) {
|
||||
this(new BufferedReader(new StringReader(text)), styleBuilder);
|
||||
}
|
||||
|
||||
|
||||
public Spannable parse() throws IOException {
|
||||
LineQueue queue = collect();
|
||||
return parse(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集 String -> LineQueue
|
||||
*
|
||||
* @return LineQueue
|
||||
* @throws IOException
|
||||
*/
|
||||
private LineQueue collect() throws IOException {
|
||||
String line;
|
||||
Line root = null;
|
||||
LineQueue queue = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!(tagHandler.imageId(line) || tagHandler.linkId(line))) {
|
||||
Line l = new Line(line);
|
||||
if (root == null) {
|
||||
root = l;
|
||||
queue = new LineQueue(root);
|
||||
} else {
|
||||
queue.append(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析LineQueue
|
||||
*
|
||||
* @param queue
|
||||
* LineQueue
|
||||
* @return Spanned
|
||||
*/
|
||||
private Spannable parse(final LineQueue queue) {
|
||||
tagHandler.setQueueProvider(new QueueConsumer.QueueProvider() {
|
||||
@Override
|
||||
public LineQueue getQueue() {
|
||||
return queue;
|
||||
}
|
||||
});
|
||||
removeCurrBlankLine(queue);
|
||||
boolean notBlock;// 当前Line不是CodeBlock
|
||||
do {
|
||||
|
||||
notBlock = queue.prevLine() != null && (queue.prevLine().getType() == Line.LINE_TYPE_OL
|
||||
|| queue.prevLine().getType() == Line.LINE_TYPE_UL || queue.prevLine().getType() == Line.LINE_TYPE_CHECKED
|
||||
|| queue.prevLine().getType() == Line.LINE_TYPE_UN_CHECKED)
|
||||
&& (tagHandler.find(Tag.UL, queue.currLine()) || tagHandler.find(Tag.OL, queue.currLine())
|
||||
|| tagHandler.find(Tag.TODO_CHECKED, queue.currLine()) || tagHandler.find(Tag.TODO_UNCHECKED, queue.currLine()));
|
||||
if (!notBlock && (tagHandler.codeBlock1(queue.currLine()) || tagHandler.codeBlock2(queue.currLine()))) {
|
||||
continue;
|
||||
}
|
||||
boolean isNewLine = tagHandler.find(Tag.NEW_LINE, queue.currLine()) || tagHandler.find(Tag.GAP, queue.currLine())
|
||||
|| tagHandler.find(Tag.H, queue.currLine());
|
||||
if (isNewLine) {
|
||||
if (queue.nextLine() != null) handleQuotaRelevant(queue, true);
|
||||
removeNextBlankLine(queue);
|
||||
} else {
|
||||
while (queue.nextLine() != null && !removeNextBlankLine(queue)) {
|
||||
if (tagHandler.find(Tag.CODE_BLOCK_1, queue.nextLine()) || tagHandler.find(Tag.CODE_BLOCK_2, queue.nextLine()) ||
|
||||
tagHandler.find(Tag.GAP, queue.nextLine()) || tagHandler.find(Tag.UL, queue.nextLine()) ||
|
||||
tagHandler.find(Tag.OL, queue.nextLine()) || tagHandler.find(Tag.H, queue.nextLine())
|
||||
|| tagHandler.find(Tag.TODO_CHECKED, queue.nextLine()) || tagHandler.find(Tag.TODO_UNCHECKED, queue.nextLine())) {
|
||||
break;
|
||||
}
|
||||
if (handleQuotaRelevant(queue, false)) break;
|
||||
}
|
||||
removeNextBlankLine(queue);
|
||||
}
|
||||
// 解析style
|
||||
if (tagHandler.gap(queue.currLine()) || tagHandler.quota(queue.currLine()) || tagHandler.ol(queue.currLine()) ||
|
||||
tagHandler.ul(queue.currLine()) || tagHandler.h(queue.currLine())) {
|
||||
continue;
|
||||
}
|
||||
queue.currLine().setStyle(SpannableStringBuilder.valueOf(queue.currLine().getSource()));
|
||||
tagHandler.inline(queue.currLine());
|
||||
} while (queue.next());
|
||||
return merge(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Quota嵌套相关问题
|
||||
*
|
||||
* @param queue
|
||||
* LineQueue
|
||||
* @param onlyH
|
||||
* 只处理Title相关的问题
|
||||
* @return true:已处理
|
||||
*/
|
||||
private boolean handleQuotaRelevant(LineQueue queue, boolean onlyH) {
|
||||
int nextQuotaCount = tagHandler.findCount(Tag.QUOTA, queue.nextLine(), 1);
|
||||
int currQuotaCount = tagHandler.findCount(Tag.QUOTA, queue.currLine(), 1);
|
||||
if (nextQuotaCount > 0 && nextQuotaCount > currQuotaCount) {
|
||||
return true;
|
||||
} else {
|
||||
String source = queue.nextLine().getSource();
|
||||
if (nextQuotaCount > 0) {
|
||||
source = source.replaceFirst("^\\s{0,3}(>\\s+){" + nextQuotaCount + "}", "");
|
||||
}
|
||||
if (currQuotaCount == nextQuotaCount) {
|
||||
if (findH1_2(queue, currQuotaCount, source)) return true;
|
||||
if (findH2_2(queue, currQuotaCount, source)) return true;
|
||||
}
|
||||
if (onlyH) {
|
||||
return false;
|
||||
}
|
||||
if (tagHandler.find(Tag.UL, source) || tagHandler.find(Tag.OL, source)
|
||||
|| tagHandler.find(Tag.TODO_CHECKED, source)
|
||||
|| tagHandler.find(Tag.TODO_UNCHECKED, source)
|
||||
|| tagHandler.find(Tag.H, source)) {
|
||||
return true;
|
||||
} else {
|
||||
queue.currLine().setSource(queue.currLine().getSource() + ' ' + source);
|
||||
queue.removeNextLine();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean findH2_2(LineQueue queue, int currQuotaCount, String source) {
|
||||
if (tagHandler.find(Tag.H2_2, source)) {
|
||||
String currLineSource = queue.currLine().getSource();
|
||||
Matcher m = Pattern.compile("^\\s{0,3}(>\\s+?){" + currQuotaCount + "}(.*)").matcher(queue.currLine().getSource());
|
||||
String newCurrLineSource;
|
||||
if (m.find()) {
|
||||
int start = m.start(2);
|
||||
int end = m.end(2);
|
||||
newCurrLineSource = currLineSource.substring(0, start) + "## " + currLineSource.subSequence(start, end);
|
||||
} else {
|
||||
newCurrLineSource = "## " + currLineSource;
|
||||
}
|
||||
queue.currLine().setSource(newCurrLineSource);
|
||||
queue.removeNextLine();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean findH1_2(LineQueue queue, int currQuotaCount, String source) {
|
||||
if (tagHandler.find(Tag.H1_2, source)) {
|
||||
String currLineSource = queue.currLine().getSource();
|
||||
Matcher m = Pattern.compile("^\\s{0,3}(>\\s+?){" + currQuotaCount + "}(.*)").matcher(currLineSource);
|
||||
String newCurrLineSource;
|
||||
if (m.find()) {
|
||||
int start = m.start(2);
|
||||
int end = m.end(2);
|
||||
newCurrLineSource = currLineSource.substring(0, start) + "# " + currLineSource.subSequence(start, end);
|
||||
} else {
|
||||
newCurrLineSource = "# " + currLineSource;
|
||||
}
|
||||
queue.currLine().setSource(newCurrLineSource);
|
||||
queue.removeNextLine();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并LineQueue -> Spanned
|
||||
*
|
||||
* @param queue
|
||||
* LineQueue
|
||||
* @return Spanned
|
||||
*/
|
||||
private Spannable merge(LineQueue queue) {
|
||||
queue.reset();
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
do {
|
||||
Line curr = queue.currLine();
|
||||
Line next = queue.nextLine();
|
||||
builder.append(curr.getStyle());
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
builder.append('\n');
|
||||
switch (curr.getType()) {
|
||||
case Line.LINE_TYPE_QUOTA:
|
||||
if (next.getType() != Line.LINE_TYPE_QUOTA) {
|
||||
builder.append('\n');
|
||||
}
|
||||
break;
|
||||
case Line.LINE_TYPE_UL:
|
||||
if (next.getType() == Line.LINE_TYPE_UL)
|
||||
builder.append(listMarginBottom());
|
||||
builder.append('\n');
|
||||
break;
|
||||
case Line.LINE_TYPE_OL:
|
||||
if (next.getType() == Line.LINE_TYPE_OL) {
|
||||
builder.append(listMarginBottom());
|
||||
}
|
||||
builder.append('\n');
|
||||
break;
|
||||
case Line.LINE_TYPE_CHECKED:
|
||||
if (next.getType() == Line.LINE_TYPE_OL) {
|
||||
builder.append(listMarginBottom());
|
||||
}
|
||||
builder.append('\n');
|
||||
break;
|
||||
case Line.LINE_TYPE_UN_CHECKED:
|
||||
if (next.getType() == Line.LINE_TYPE_OL) {
|
||||
builder.append(listMarginBottom());
|
||||
}
|
||||
builder.append('\n');
|
||||
break;
|
||||
default:
|
||||
builder.append('\n');
|
||||
}
|
||||
} while (queue.next());
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从下个Line开始移除空Line
|
||||
*
|
||||
* @param queue
|
||||
* LineQueue
|
||||
* @return 是否移除了
|
||||
*/
|
||||
private boolean removeNextBlankLine(LineQueue queue) {
|
||||
boolean flag = false;
|
||||
while (queue.nextLine() != null) {
|
||||
if (tagHandler.find(Tag.BLANK, queue.nextLine())) {
|
||||
queue.removeNextLine();
|
||||
flag = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前行开始移除空Line
|
||||
*
|
||||
* @param queue
|
||||
* LineQueue
|
||||
* @return true:移除了Line
|
||||
*/
|
||||
private boolean removeCurrBlankLine(LineQueue queue) {
|
||||
boolean flag = false;
|
||||
while (queue.currLine() != null) {
|
||||
if (tagHandler.find(Tag.BLANK, queue.currLine())) {
|
||||
queue.removeCurrLine();
|
||||
flag = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
||||
private SpannableString listMarginBottom() {
|
||||
SpannableString ss = new SpannableString(" ");
|
||||
ss.setSpan(new ScaleHeightSpan(0.4f), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return ss;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,412 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-28.
|
||||
* 代表每一行文本
|
||||
*/
|
||||
public class Line {
|
||||
|
||||
public static final int LINE_NORMAL = 0; // 普通文本
|
||||
public static final int LINE_TYPE_QUOTA = 1; // 引用
|
||||
public static final int LINE_TYPE_UL = 2; // 无序列表
|
||||
public static final int LINE_TYPE_OL = 3; // 有序列表
|
||||
public static final int LINE_TYPE_H1 = 4; // H1
|
||||
public static final int LINE_TYPE_H2 = 5; // H2
|
||||
public static final int LINE_TYPE_H3 = 6; // H3
|
||||
public static final int LINE_TYPE_H4 = 7; // H4
|
||||
public static final int LINE_TYPE_H5 = 8; // H5
|
||||
public static final int LINE_TYPE_H6 = 9; // H6
|
||||
public static final int LINE_TYPE_CODE_BLOCK_2 = 10;
|
||||
public static final int LINE_TYPE_CODE_BLOCK_1 = 11;
|
||||
public static final int LINE_TYPE_GAP = 12;
|
||||
public static final int LINE_TYPE_CHECKED = 13;
|
||||
public static final int LINE_TYPE_UN_CHECKED = 14;
|
||||
public static final int HANDLE_BY_ROOT = 1;
|
||||
|
||||
private Line prev; // 前一个节点
|
||||
private Line next; // 下一个节点
|
||||
private Line parent; // 父节点
|
||||
private Line child; // 子节点
|
||||
|
||||
private String source; // 源文本
|
||||
private CharSequence style; // 样式
|
||||
private int type; // 种类
|
||||
private int count; // 数量
|
||||
private int attr; // 属性
|
||||
private int handle; // 0
|
||||
private int data;
|
||||
|
||||
|
||||
public Line(String source) {
|
||||
this.source = source;
|
||||
count = 1;
|
||||
type = LINE_NORMAL;
|
||||
}
|
||||
|
||||
private Line(Line line) {
|
||||
this.source = line.source;
|
||||
this.count = line.count;
|
||||
this.attr = line.attr;
|
||||
if (line.style != null) {
|
||||
this.style = new SpannableStringBuilder(line.style);
|
||||
}
|
||||
this.type = line.type;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setStyle(CharSequence style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public CharSequence getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void setHandle(int handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public int getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(int data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setAttr(int attr) {
|
||||
this.attr = attr;
|
||||
}
|
||||
|
||||
public int getAttr() {
|
||||
return attr;
|
||||
}
|
||||
|
||||
public Line get() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Line nextLine() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public Line prevLine() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
public Line childLine() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public Line parentLine() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加为下一个节点
|
||||
*
|
||||
* @param line
|
||||
* Line
|
||||
* @return Line
|
||||
*/
|
||||
public Line addNext(Line line) {
|
||||
if (line == null) {
|
||||
next = null;
|
||||
} else {
|
||||
if (line.next != null) {
|
||||
line.next.prev = null;
|
||||
}
|
||||
line.next = next;
|
||||
if (next != null) {
|
||||
next.prev = line;
|
||||
}
|
||||
if (line.prev != null) {
|
||||
line.prev.next = null;
|
||||
}
|
||||
line.prev = this;
|
||||
next = line;
|
||||
if (child != null) {
|
||||
child.addNext(line.child);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加为上一个节点
|
||||
*
|
||||
* @param line
|
||||
* Line
|
||||
* @return Line
|
||||
*/
|
||||
public Line addPrev(Line line) {
|
||||
if (line == null) {
|
||||
prev = null;
|
||||
} else {
|
||||
if (line.prev != null) {
|
||||
line.prev.next = null;
|
||||
}
|
||||
line.prev = prev;
|
||||
if (prev != null) {
|
||||
prev.next = line;
|
||||
}
|
||||
if (line.next != null) {
|
||||
line.next.prev = null;
|
||||
}
|
||||
line.next = this;
|
||||
prev = line;
|
||||
if (child != null) {
|
||||
child.addPrev(line.child);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
public Line add(Line line) {
|
||||
return addNext(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前节点,包括其子节点包(会导致链表断开)
|
||||
*/
|
||||
private void delete() {
|
||||
if (child != null) {
|
||||
child.delete();
|
||||
}
|
||||
if (prev != null) {
|
||||
prev.next = null;
|
||||
}
|
||||
prev = null;
|
||||
if (next != null) {
|
||||
next.prev = null;
|
||||
}
|
||||
next = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除当前节点,重新连上前后两个节点,包括其子节点(不会导致链表断开)
|
||||
*/
|
||||
private void reduce() {
|
||||
if (child != null) {
|
||||
child.reduce();
|
||||
}
|
||||
if (prev != null) {
|
||||
prev.next = next;
|
||||
}
|
||||
if (next != null) {
|
||||
next.prev = prev;
|
||||
}
|
||||
next = null;
|
||||
prev = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除当前节点并且不会导致主链表断开
|
||||
*/
|
||||
public void remove() {
|
||||
if (parent == null) {
|
||||
reduce();
|
||||
} else {
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除下一个节点
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Line removeNext() {
|
||||
if (next != null) {
|
||||
next.remove();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除上一行
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Line removePrev() {
|
||||
if (prev != null) {
|
||||
prev.remove();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子节点,并将子节点和前后节点的子节点建立连接
|
||||
*
|
||||
* @param line
|
||||
* child
|
||||
*/
|
||||
public void addChild(Line line) {
|
||||
if (child != null) {
|
||||
child.parent = null;
|
||||
}
|
||||
child = line;
|
||||
if (line.parent != null) {
|
||||
line.parent.child = null;
|
||||
}
|
||||
line.parent = this;
|
||||
attachChildToNext();
|
||||
attachChildToPrev();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将子节点和下一个节点之间建立连接
|
||||
*/
|
||||
public void attachChildToNext() {
|
||||
if (child != null && next != null) {
|
||||
if (child.next != null) {
|
||||
child.next.prev = null;
|
||||
}
|
||||
child.next = next.child;
|
||||
if (next.child != null) {
|
||||
if (next.child.prev != null) {
|
||||
next.child.prev.next = null;
|
||||
}
|
||||
next.child.prev = child;
|
||||
}
|
||||
child.attachChildToNext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将子节点和上一个节点之间建立连接
|
||||
*/
|
||||
public void attachChildToPrev() {
|
||||
if (child != null && prev != null) {
|
||||
if (child.prev != null) {
|
||||
child.prev.next = null;
|
||||
}
|
||||
child.prev = prev.child;
|
||||
if (prev.child != null) {
|
||||
if (prev.child.next != null) {
|
||||
prev.child.next.prev = null;
|
||||
}
|
||||
prev.child.next = child;
|
||||
}
|
||||
child.attachChildToPrev();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自身作为子节点添加到某个节点
|
||||
*
|
||||
* @param line
|
||||
* parent
|
||||
*/
|
||||
public void attachToParent(Line line) {
|
||||
line.addChild(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从父节点上解绑
|
||||
*/
|
||||
public void unAttachFromParent() {
|
||||
if (parent != null) {
|
||||
delete();
|
||||
parent.child = null;
|
||||
}
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个子节点
|
||||
*
|
||||
* @param src
|
||||
* 子节点的source
|
||||
* @return 子节点
|
||||
*/
|
||||
public Line createChild(String src) {
|
||||
Line c = new Line(src);
|
||||
addChild(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自身及父节点copy为下一个节点
|
||||
*
|
||||
* @return copy产生的对象
|
||||
*/
|
||||
public Line copyToNext() {
|
||||
Line p = null;
|
||||
if (parent != null) {
|
||||
p = parent.copyToNext();
|
||||
}
|
||||
Line line = new Line(this);
|
||||
if (p == null) {
|
||||
line.next = next;
|
||||
if (next != null) {
|
||||
next.prev = line;
|
||||
}
|
||||
line.prev = this;
|
||||
next = line;
|
||||
} else {
|
||||
p.addChild(line);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自身及父节点copy为上一个节点
|
||||
*
|
||||
* @return copy产生的对象
|
||||
*/
|
||||
public Line copyToPrev() {
|
||||
Line p = null;
|
||||
if (parent != null) {
|
||||
p = parent.copyToPrev();
|
||||
}
|
||||
Line line = new Line(this);
|
||||
if (p == null) {
|
||||
line.prev = prev;
|
||||
if (prev != null) {
|
||||
prev.next = line;
|
||||
}
|
||||
line.next = this;
|
||||
prev = this;
|
||||
} else {
|
||||
p.addChild(line);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-2.
|
||||
*/
|
||||
public class LineQueue {
|
||||
|
||||
private Line root; // 根节点
|
||||
private Line curr; // 当前节点
|
||||
private Line last; // 尾节点
|
||||
|
||||
public LineQueue(Line root) {
|
||||
this.root = root;
|
||||
curr = root;
|
||||
last = root;
|
||||
while (last.nextLine() != null) {
|
||||
last = last.nextLine();
|
||||
}
|
||||
}
|
||||
|
||||
private LineQueue(LineQueue queue, Line curr) {
|
||||
this.root = queue.root;
|
||||
this.last = queue.last;
|
||||
this.curr = curr;
|
||||
}
|
||||
|
||||
public Line nextLine() {
|
||||
return curr.nextLine();
|
||||
}
|
||||
|
||||
public Line prevLine() {
|
||||
return curr.prevLine();
|
||||
}
|
||||
|
||||
public Line currLine() {
|
||||
return curr;
|
||||
}
|
||||
|
||||
public boolean next() {
|
||||
if (curr.nextLine() == null) {
|
||||
return false;
|
||||
}
|
||||
curr = curr.nextLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean prev() {
|
||||
if (curr.prevLine() == null) {
|
||||
return false;
|
||||
}
|
||||
curr = currLine().prevLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return curr.nextLine() == null;
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
return curr == root;
|
||||
}
|
||||
|
||||
public void append(Line line) {
|
||||
last.add(line);
|
||||
last = line;
|
||||
}
|
||||
|
||||
public void insert(Line line) {
|
||||
if (curr == last) {
|
||||
append(line);
|
||||
} else {
|
||||
curr.addNext(line);
|
||||
}
|
||||
}
|
||||
|
||||
public Line removeCurrLine() {
|
||||
Line tmp;
|
||||
if (curr == last) {
|
||||
tmp = last.prevLine();
|
||||
} else {
|
||||
tmp = curr.nextLine();
|
||||
if (curr == root) {
|
||||
root = tmp;
|
||||
}
|
||||
}
|
||||
curr.remove();
|
||||
Line r = curr;
|
||||
curr = tmp;
|
||||
return r;
|
||||
}
|
||||
|
||||
public void removeNextLine() {
|
||||
curr.removeNext();
|
||||
}
|
||||
|
||||
public void removePrevLine() {
|
||||
if (root == curr.prevLine()) {
|
||||
root = curr;
|
||||
}
|
||||
curr.removePrev();
|
||||
}
|
||||
|
||||
public LineQueue copy() {
|
||||
return new LineQueue(this, curr);
|
||||
}
|
||||
|
||||
public LineQueue copyNext() {
|
||||
if (end()) {
|
||||
return null;
|
||||
}
|
||||
return new LineQueue(this, curr.nextLine());
|
||||
}
|
||||
|
||||
|
||||
public void reset() {
|
||||
curr = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Line t = root;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int len = 0;
|
||||
while (t != null) {
|
||||
sb.append(t.toString()).append(",");
|
||||
t = t.nextLine();
|
||||
len++;
|
||||
}
|
||||
return "{" + sb.toString() + "}";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-22.
|
||||
* LineQueue的消费者
|
||||
*/
|
||||
public interface QueueConsumer {
|
||||
|
||||
|
||||
void setQueueProvider(QueueProvider provider);
|
||||
|
||||
/**
|
||||
* LineQueue提供器
|
||||
*/
|
||||
interface QueueProvider {
|
||||
LineQueue getQueue();
|
||||
}
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-28.
|
||||
* markdown各种样式的构建器
|
||||
*/
|
||||
public interface StyleBuilder {
|
||||
|
||||
SpannableStringBuilder em(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder italic(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder emItalic(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder delete(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder email(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder link(CharSequence title, String link, String hint);
|
||||
|
||||
SpannableStringBuilder image(CharSequence title, String url, String hint);
|
||||
|
||||
SpannableStringBuilder code(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h1(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h2(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h3(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h4(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h5(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder h6(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder quota(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder ul(CharSequence charSequence, int level);
|
||||
|
||||
SpannableStringBuilder ol(CharSequence charSequence, int level, int index);
|
||||
|
||||
SpannableStringBuilder ul2(CharSequence charSequence, int quotaLevel, int bulletLevel);
|
||||
|
||||
SpannableStringBuilder ol2(CharSequence charSequence, int quotaLevel, int bulletLevel, int index);
|
||||
|
||||
SpannableStringBuilder codeBlock(CharSequence... charSequence);
|
||||
|
||||
SpannableStringBuilder codeBlock(String code);
|
||||
|
||||
SpannableStringBuilder gap();
|
||||
|
||||
SpannableStringBuilder checked(CharSequence charSequence);
|
||||
|
||||
SpannableStringBuilder unChecked(CharSequence charSequence);
|
||||
|
||||
}
|
||||
@ -1,282 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zzhoujay.markdown.style.CodeBlockSpan;
|
||||
import com.zzhoujay.markdown.style.EmailSpan;
|
||||
import com.zzhoujay.markdown.style.FontSpan;
|
||||
import com.zzhoujay.markdown.style.LinkSpan;
|
||||
import com.zzhoujay.markdown.style.MarkDownBulletSpan;
|
||||
import com.zzhoujay.markdown.style.MarkDownQuoteSpan;
|
||||
import com.zzhoujay.markdown.style.QuotaBulletSpan;
|
||||
import com.zzhoujay.markdown.style.UnderLineSpan;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-28.
|
||||
* StyleBuilderImpl
|
||||
*/
|
||||
public class StyleBuilderImpl implements StyleBuilder {
|
||||
|
||||
private int h1_color = Color.parseColor("#333333");
|
||||
private static final int h6_color = Color.parseColor("#777777");
|
||||
private static final int quota_color = Color.parseColor("#DDDDDD");
|
||||
private int code_color = Color.parseColor("#F0F0F0");
|
||||
private static final int link_color = Color.parseColor("#4078C0");
|
||||
private static final int h_under_line_color = Color.parseColor("#eeeeee");
|
||||
|
||||
private static final float scale_h1 = 2.25f;
|
||||
private static final float scale_h2 = 1.75f;
|
||||
private static final float scale_h3 = 1.5f;
|
||||
private static final float scale_h4 = 1.25f;
|
||||
private static final float scale_h5 = 1, scale_h6 = 1;
|
||||
private static final float scale_normal = 1;
|
||||
|
||||
private WeakReference<TextView> textViewWeakReference;
|
||||
private Html.ImageGetter imageGetter;
|
||||
|
||||
public StyleBuilderImpl(TextView textView, int codeColor, int h1_color, Html.ImageGetter imageGetter) {
|
||||
this.textViewWeakReference = new WeakReference<>(textView);
|
||||
this.code_color = codeColor;
|
||||
this.h1_color = h1_color;
|
||||
this.imageGetter = imageGetter;
|
||||
}
|
||||
|
||||
public StyleBuilderImpl(TextView textView, Html.ImageGetter imageGetter) {
|
||||
this.textViewWeakReference = new WeakReference<>(textView);
|
||||
this.imageGetter = imageGetter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder em(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.BOLD, h1_color);
|
||||
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder italic(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.ITALIC, h1_color);
|
||||
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder emItalic(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
FontSpan fontSpan = new FontSpan(scale_normal, Typeface.BOLD_ITALIC, h1_color);
|
||||
builder.setSpan(fontSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder delete(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
StrikethroughSpan span = new StrikethroughSpan();
|
||||
ForegroundColorSpan colorSpan = new ForegroundColorSpan(h1_color);
|
||||
builder.setSpan(colorSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(span, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder email(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
EmailSpan emailSpan = new EmailSpan(charSequence.toString(), link_color);
|
||||
builder.setSpan(emailSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder code(CharSequence charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(charSequence);
|
||||
builder.setSpan(new BackgroundColorSpan(code_color), 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(new TypefaceSpan("monospace"), 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h1(CharSequence charSequence) {
|
||||
return hWithUnderLine(charSequence, scale_h1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h2(CharSequence charSequence) {
|
||||
return hWithUnderLine(charSequence, scale_h2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h3(CharSequence charSequence) {
|
||||
return h(charSequence, scale_h3, h1_color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h4(CharSequence charSequence) {
|
||||
return h(charSequence, scale_h4, h1_color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h5(CharSequence charSequence) {
|
||||
return h(charSequence, scale_h5, h1_color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder h6(CharSequence charSequence) {
|
||||
return h(charSequence, scale_h6, h6_color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder quota(CharSequence charSequence) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
QuoteSpan span = new MarkDownQuoteSpan(quota_color);
|
||||
spannableStringBuilder.setSpan(span, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder ul(CharSequence charSequence, int level) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
BulletSpan bulletSpan = new MarkDownBulletSpan(level, h1_color, 0);
|
||||
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder ol(CharSequence charSequence, int level, int index) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
BulletSpan bulletSpan = new MarkDownBulletSpan(level, h1_color, index, textViewWeakReference.get());
|
||||
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder ul2(CharSequence charSequence, int quotaLevel, int bulletLevel) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
QuotaBulletSpan bulletSpan = new QuotaBulletSpan(quotaLevel, bulletLevel, quota_color, h1_color, 0, textViewWeakReference.get());
|
||||
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder ol2(CharSequence charSequence, int quotaLevel, int bulletLevel, int index) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
QuotaBulletSpan bulletSpan = new QuotaBulletSpan(quotaLevel, bulletLevel, quota_color, h1_color, index, textViewWeakReference.get());
|
||||
spannableStringBuilder.setSpan(bulletSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder codeBlock(CharSequence... charSequence) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf("$");
|
||||
CodeBlockSpan codeBlockSpan = new CodeBlockSpan(getTextViewRealWidth(), code_color, charSequence);
|
||||
builder.setSpan(codeBlockSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
builder.setSpan(new TypefaceSpan("monospace"), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder codeBlock(String code) {
|
||||
return codeBlock((CharSequence[]) code.split("\n"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder link(CharSequence title, String link, String hint) {
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(title);
|
||||
LinkSpan linkSpan = new LinkSpan(link, link_color);
|
||||
builder.setSpan(linkSpan, 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder image(CharSequence title, String url, String hint) {
|
||||
if (title == null || title.length() == 0) {
|
||||
title = "$";
|
||||
}
|
||||
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(title);
|
||||
Drawable drawable = null;
|
||||
if (imageGetter != null) {
|
||||
drawable = imageGetter.getDrawable(url);
|
||||
}
|
||||
if (drawable == null) {
|
||||
drawable = new ColorDrawable(Color.TRANSPARENT);
|
||||
// builder.delete(0, builder.length());
|
||||
// return builder;
|
||||
}
|
||||
ImageSpan imageSpan = new ImageSpan(drawable, url);
|
||||
builder.setSpan(imageSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected SpannableStringBuilder h(CharSequence charSequence, float s, int color) {
|
||||
SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(charSequence);
|
||||
FontSpan fontSpan = new FontSpan(s, Typeface.BOLD, color);
|
||||
spannableStringBuilder.setSpan(fontSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
private SpannableStringBuilder hWithUnderLine(CharSequence charSequence, float s) {
|
||||
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(charSequence);
|
||||
int start = 0;
|
||||
FontSpan fontSpan = new FontSpan(s, Typeface.BOLD, h1_color);
|
||||
spannableStringBuilder.setSpan(fontSpan, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
Drawable underLine = new ColorDrawable(h_under_line_color);
|
||||
UnderLineSpan underLineSpan = new UnderLineSpan(underLine, getTextViewRealWidth(), 5);
|
||||
spannableStringBuilder.append('\n');
|
||||
start += charSequence.length() + 1;
|
||||
spannableStringBuilder.append("$");
|
||||
spannableStringBuilder.setSpan(underLineSpan, start, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
private int getTextViewRealWidth() {
|
||||
TextView textView = textViewWeakReference.get();
|
||||
if (textView != null) {
|
||||
return textView.getWidth() - textView.getPaddingRight() - textView.getPaddingLeft();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableStringBuilder gap() {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder("$");
|
||||
Drawable underLine = new ColorDrawable(h_under_line_color);
|
||||
UnderLineSpan underLineSpan = new UnderLineSpan(underLine, getTextViewRealWidth(), 10);
|
||||
builder.setSpan(underLineSpan, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override public SpannableStringBuilder checked(CharSequence charSequence) {
|
||||
Log.e("checked()", "" + charSequence);
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
return builder.append("☑")
|
||||
.append("\t")
|
||||
.append(charSequence);
|
||||
}
|
||||
|
||||
@Override public SpannableStringBuilder unChecked(CharSequence charSequence) {
|
||||
Log.e("unChecked()", "" + charSequence);
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
return builder.append("☐")
|
||||
.append("\t")
|
||||
.append(charSequence);
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-10.
|
||||
*/
|
||||
public class Tag {
|
||||
|
||||
public static final int NORMAL = 0;
|
||||
public static final int CODE_BLOCK_1 = 1;
|
||||
public static final int CODE_BLOCK_2 = 2;
|
||||
public static final int H1 = 3;
|
||||
public static final int H2 = 4;
|
||||
public static final int H3 = 24;
|
||||
public static final int H4 = 5;
|
||||
public static final int H5 = 6;
|
||||
public static final int H6 = 7;
|
||||
public static final int QUOTA = 8;
|
||||
public static final int UL = 9;
|
||||
public static final int OL = 10;
|
||||
public static final int EM = 11;
|
||||
public static final int ITALIC = 12;
|
||||
public static final int EM_ITALIC = 13;
|
||||
public static final int EMAIL = 14;
|
||||
public static final int AUTO_LINK = 15;
|
||||
public static final int DELETE = 16;
|
||||
public static final int LINK = 17;
|
||||
public static final int LINK2 = 18;
|
||||
public static final int LINK_ID = 19;
|
||||
public static final int IMAGE = 20;
|
||||
public static final int IMAGE2 = 21;
|
||||
public static final int IMAGE_ID = 22;
|
||||
public static final int H = 23;
|
||||
public static final int BLANK = 25;
|
||||
public static final int NEW_LINE = 26;
|
||||
public static final int GAP = 27;
|
||||
public static final int H1_2 = 28;
|
||||
public static final int H2_2 = 29;
|
||||
public static final int CODE = 30;
|
||||
public static final int TODO_CHECKED = 31;
|
||||
public static final int TODO_UNCHECKED = 32;
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-10.
|
||||
* TagFinder
|
||||
*/
|
||||
public interface TagFinder {
|
||||
|
||||
/**
|
||||
* 检查对应tag是否存在
|
||||
*
|
||||
* @param tag Tag Id
|
||||
* @param line line
|
||||
* @return true:存在,false不存在
|
||||
*/
|
||||
boolean find(int tag, Line line);
|
||||
|
||||
/**
|
||||
* 检查对应tag是否存在
|
||||
*
|
||||
* @param tag Tag Id
|
||||
* @param line line
|
||||
* @return true:存在,false不存在
|
||||
*/
|
||||
boolean find(int tag, String line);
|
||||
|
||||
/**
|
||||
* 检查对应tag的个数
|
||||
*
|
||||
* @param tag tag id
|
||||
* @param line line
|
||||
* @param group group
|
||||
* @return 对应tag的次数
|
||||
*/
|
||||
int findCount(int tag, Line line, int group);
|
||||
|
||||
/**
|
||||
* 检查对应tag的个数
|
||||
*
|
||||
* @param tag tag id
|
||||
* @param line line
|
||||
* @param group group
|
||||
* @return 对应tag的次数
|
||||
*/
|
||||
int findCount(int tag, String line, int group);
|
||||
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* 获取Tag内容
|
||||
*/
|
||||
public interface TagGetter {
|
||||
|
||||
/**
|
||||
* 获取Line特定Tag的内容
|
||||
*
|
||||
* @param tag tag
|
||||
* @param line Line
|
||||
* @param group group
|
||||
* @return content
|
||||
*/
|
||||
CharSequence get(int tag, Line line, int group);
|
||||
|
||||
/**
|
||||
* 获取line特定Tag的内容
|
||||
*
|
||||
* @param tag tag
|
||||
* @param line line
|
||||
* @param group group
|
||||
* @return content
|
||||
*/
|
||||
CharSequence get(int tag, CharSequence line, int group);
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-10.
|
||||
* TagHandler
|
||||
*/
|
||||
public interface TagHandler extends TagFinder, QueueConsumer, TagGetter {
|
||||
|
||||
boolean h(Line line);
|
||||
|
||||
boolean h1(Line line);
|
||||
|
||||
boolean h2(Line line);
|
||||
|
||||
boolean h3(Line line);
|
||||
|
||||
boolean h4(Line line);
|
||||
|
||||
boolean h5(Line line);
|
||||
|
||||
boolean h6(Line line);
|
||||
|
||||
boolean quota(Line line);
|
||||
|
||||
boolean ul(Line line);
|
||||
|
||||
boolean ol(Line line);
|
||||
|
||||
boolean todoChecked(Line line);
|
||||
|
||||
boolean todoUnChecked(Line line);
|
||||
|
||||
boolean gap(Line line);
|
||||
|
||||
boolean em(Line line);
|
||||
|
||||
boolean italic(Line line);
|
||||
|
||||
boolean emItalic(Line line);
|
||||
|
||||
boolean code(Line line);
|
||||
|
||||
boolean email(Line line);
|
||||
|
||||
boolean delete(Line line);
|
||||
|
||||
boolean autoLink(Line line);
|
||||
|
||||
boolean link(Line line);
|
||||
|
||||
boolean link2(Line line);
|
||||
|
||||
boolean linkId(String line);
|
||||
|
||||
boolean image(Line line);
|
||||
|
||||
boolean image2(Line line);
|
||||
|
||||
boolean imageId(String line);
|
||||
|
||||
boolean inline(Line line);
|
||||
|
||||
boolean codeBlock1(Line line);
|
||||
|
||||
boolean codeBlock2(Line line);
|
||||
|
||||
}
|
||||
@ -1,952 +0,0 @@
|
||||
package com.zzhoujay.markdown.parser;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.zzhoujay.markdown.style.CodeSpan;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-10.
|
||||
* TagHandlerImpl
|
||||
*/
|
||||
public class TagHandlerImpl implements TagHandler {
|
||||
|
||||
private static final Matcher matcherH1_2 = Pattern.compile("^\\s*=+\\s*$").matcher("");
|
||||
private static final Matcher matcherH2_2 = Pattern.compile("^\\s*-+\\s*$").matcher("");
|
||||
|
||||
private static final Matcher matcherH = Pattern.compile("^\\s*#{1,6}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH1 = Pattern.compile("^\\s*#\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH2 = Pattern.compile("^\\s*#{2}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH3 = Pattern.compile("^\\s*#{3}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH4 = Pattern.compile("^\\s*#{4}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH5 = Pattern.compile("^\\s*#{5}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
private static final Matcher matcherH6 = Pattern.compile("^\\s*#{6}\\s*([^#]*)(\\s*#)?").matcher("");
|
||||
|
||||
private static final Matcher matcherQuota = Pattern.compile("^\\s{0,3}>\\s(.*)").matcher("");
|
||||
private static final Matcher matcherUl = Pattern.compile("^\\s*[*+-]\\s+(.*)").matcher("");
|
||||
private static final Matcher matcherOl = Pattern.compile("^\\s*\\d+\\.\\s+(.*)").matcher("");
|
||||
private static final Matcher matcherUnChecked = Pattern.compile("-\\s\\[ ]+(.*)").matcher("");
|
||||
private static final Matcher matcherChecked = Pattern.compile("-\\s\\[x]+(.*)").matcher("");
|
||||
private static final Matcher matcherItalic = Pattern.compile("[^*_]*(([*_])([^*_].*?)\\2)").matcher("");
|
||||
private static final Matcher matcherEm = Pattern.compile("[^*_]*(([*_])\\2([^*_].*?)\\2\\2)").matcher("");
|
||||
private static final Matcher matcherEmItalic = Pattern.compile("[^*_]*(([*_])\\2\\2([^*_].*?)\\2\\2\\2)").matcher("");
|
||||
private static final Matcher matcherDelete = Pattern.compile("[^~]*((~{2,4})([^~].*?)\\2)").matcher("");
|
||||
private static final Matcher matcherCode = Pattern.compile("[^`]*((`+)([^`].*?)\\2)").matcher("");
|
||||
|
||||
private static final Matcher matcherLink = Pattern.compile(".*?(\\[\\s*(.*?)\\s*]\\(\\s*(\\S*?)(\\s+(['\"])(.*?)\\5)?\\s*?\\))").matcher("");
|
||||
private static final Matcher matcherImage = Pattern.compile(".*?(!\\[\\s*(.*?)\\s*]\\(\\s*(\\S*?)(\\s+(['\"])(.*?)\\5)?\\s*?\\))").matcher("");
|
||||
private static final Matcher matcherLink2 = Pattern.compile(".*?(\\[\\s*(.*?)\\s*]\\s*\\[\\s*(.*?)\\s*])").matcher("");
|
||||
private static final Matcher matcherLinkId = Pattern.compile("^\\s*\\[\\s*(.*?)\\s*]:\\s*(\\S+?)(\\s+(['\"])(.*?)\\4)?\\s*$").matcher("");
|
||||
private static final Matcher matcherImage2 = Pattern.compile(".*?(!\\[\\s*(.*?)\\s*]\\s*\\[\\s*(.*?)\\s*])").matcher("");
|
||||
private static final Matcher matcherImageId = Pattern.compile("^\\s*!\\[\\s*(.*?)\\s*]:\\s*(\\S+?)(\\s+(['\"])(.*?)\\4)?\\s*$").matcher("");
|
||||
|
||||
private static final Matcher matcherEmail = Pattern.compile(".*?(<(\\S+@\\S+\\.\\S+)>).*?").matcher("");
|
||||
private static final Matcher matcherAutoLink = Pattern.compile("((https|http|ftp|rtsp|mms)?://)[^\\s]+").matcher("");
|
||||
|
||||
private static final Matcher matcherEndSpace = Pattern.compile("(.*?) {2} *$").matcher("");
|
||||
private static final Matcher matcherInlineSpace = Pattern.compile("\\S*(\\s+)\\S+").matcher("");
|
||||
|
||||
private static final Matcher matcherCodeBlock = Pattern.compile("^( {4}|\\t)(.*)").matcher("");
|
||||
private static final Matcher matcherCodeBlock2 = Pattern.compile("^\\s*```").matcher("");
|
||||
|
||||
private static final Matcher matcherBlankLine = Pattern.compile("^\\s*$").matcher("");
|
||||
|
||||
private static final Matcher matcherGap = Pattern.compile("^\\s*([-*]\\s*){3,}$").matcher("");
|
||||
|
||||
private static final SparseArray<Matcher> matchers = new SparseArray<>();// matcher缓冲区
|
||||
|
||||
static {
|
||||
matchers.put(Tag.CODE_BLOCK_1, matcherCodeBlock);
|
||||
matchers.put(Tag.CODE_BLOCK_2, matcherCodeBlock2);
|
||||
matchers.put(Tag.H1, matcherH1);
|
||||
matchers.put(Tag.H2, matcherH2);
|
||||
matchers.put(Tag.H3, matcherH3);
|
||||
matchers.put(Tag.H4, matcherH4);
|
||||
matchers.put(Tag.H5, matcherH5);
|
||||
matchers.put(Tag.H6, matcherH6);
|
||||
matchers.put(Tag.H, matcherH);
|
||||
matchers.put(Tag.TODO_CHECKED, matcherChecked);
|
||||
matchers.put(Tag.TODO_UNCHECKED, matcherUnChecked);
|
||||
matchers.put(Tag.QUOTA, matcherQuota);
|
||||
matchers.put(Tag.UL, matcherUl);
|
||||
matchers.put(Tag.OL, matcherOl);
|
||||
matchers.put(Tag.EM, matcherEm);
|
||||
matchers.put(Tag.ITALIC, matcherItalic);
|
||||
matchers.put(Tag.EM_ITALIC, matcherEmItalic);
|
||||
matchers.put(Tag.EMAIL, matcherEmail);
|
||||
matchers.put(Tag.AUTO_LINK, matcherAutoLink);
|
||||
matchers.put(Tag.DELETE, matcherDelete);
|
||||
matchers.put(Tag.LINK, matcherLink);
|
||||
matchers.put(Tag.LINK2, matcherLink2);
|
||||
matchers.put(Tag.LINK_ID, matcherLinkId);
|
||||
matchers.put(Tag.IMAGE, matcherImage);
|
||||
matchers.put(Tag.IMAGE2, matcherImage2);
|
||||
matchers.put(Tag.IMAGE_ID, matcherImageId);
|
||||
matchers.put(Tag.BLANK, matcherBlankLine);
|
||||
matchers.put(Tag.NEW_LINE, matcherEndSpace);
|
||||
matchers.put(Tag.GAP, matcherGap);
|
||||
matchers.put(Tag.H1_2, matcherH1_2);
|
||||
matchers.put(Tag.H2_2, matcherH2_2);
|
||||
matchers.put(Tag.CODE, matcherCode);
|
||||
}
|
||||
|
||||
private StyleBuilder styleBuilder;
|
||||
private QueueProvider queueProvider;
|
||||
private HashMap<String, Pair<String, String>> idLinkLinks;
|
||||
private HashMap<String, Pair<String, String>> idImageUrl;
|
||||
|
||||
|
||||
public TagHandlerImpl(StyleBuilder styleBuilder) {
|
||||
this.styleBuilder = styleBuilder;
|
||||
idImageUrl = new HashMap<>();
|
||||
idLinkLinks = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h(Line line) {
|
||||
return h6(line) || h5(line) || h4(line) || h3(line) || h2(line) || h1(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h1(Line line) {
|
||||
Matcher matcher = obtain(Tag.H1, line.getSource());
|
||||
if (matcher != null && matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H1);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h1(line.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h2(Line line) {
|
||||
Matcher matcher = obtain(Tag.H2, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H2);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h2(line.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h3(Line line) {
|
||||
Matcher matcher = obtain(Tag.H3, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H3);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h3(line.getStyle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h4(Line line) {
|
||||
Matcher matcher = obtain(Tag.H4, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H4);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h4(line.getStyle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h5(Line line) {
|
||||
Matcher matcher = obtain(Tag.H5, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H5);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h5(line.getStyle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean h6(Line line) {
|
||||
Matcher matcher = obtain(Tag.H6, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_H6);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.h6(line.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean quota(Line line) {
|
||||
LineQueue queue = queueProvider.getQueue();
|
||||
line = line.get();
|
||||
|
||||
Matcher matcher = obtain(Tag.QUOTA, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_QUOTA);
|
||||
Line child = line.createChild(matcher.group(1));
|
||||
line.attachChildToNext();
|
||||
line.attachChildToPrev();
|
||||
|
||||
Line prev = queue.prevLine();
|
||||
if (line.parentLine() == null && prev != null && prev.getType() == Line.LINE_TYPE_QUOTA) {
|
||||
SpannableStringBuilder style = new SpannableStringBuilder(" ");
|
||||
styleBuilder.quota(style);
|
||||
while (prev.childLine() != null && prev.childLine().getType() == Line.LINE_TYPE_QUOTA) {
|
||||
prev = prev.childLine();
|
||||
styleBuilder.quota(style);
|
||||
}
|
||||
prev.copyToNext();
|
||||
queue.prevLine().setStyle(style);
|
||||
}
|
||||
if (quota(child) || ul(child) || ol(child) || h(child)) {
|
||||
if (child.getHandle() == Line.HANDLE_BY_ROOT) {
|
||||
if (line.parentLine() == null) {
|
||||
if (child.getData() == Line.LINE_TYPE_UL) {
|
||||
line.setStyle(styleBuilder.ul2(child.getStyle(), findCount(Tag.QUOTA, line, 1) - 1, child.getAttr()));
|
||||
} else {
|
||||
line.setStyle(styleBuilder.ol2(child.getStyle(), findCount(Tag.QUOTA, line, 1) - 1, child.getAttr(), child.getCount()));
|
||||
}
|
||||
} else {
|
||||
line.setData(child.getData());
|
||||
line.setStyle(child.getStyle());
|
||||
line.setAttr(child.getAttr());
|
||||
line.setCount(child.getCount());
|
||||
line.setHandle(Line.HANDLE_BY_ROOT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
child.setStyle(SpannableStringBuilder.valueOf(child.getSource()));
|
||||
inline(child);
|
||||
}
|
||||
|
||||
line.setStyle(styleBuilder.quota(child.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ul(Line line) {
|
||||
return ul(line, 0);
|
||||
}
|
||||
|
||||
private boolean ul(Line line, int level) {
|
||||
if (line.getSource() != null && (line.getSource().startsWith("- [ ]") || line.getSource().startsWith("- [x]"))) {
|
||||
if (line.getSource().startsWith("- [x]")) {
|
||||
return todoChecked(line);
|
||||
} else {
|
||||
return todoUnChecked(line);
|
||||
}
|
||||
}
|
||||
Matcher matcher = obtain(Tag.UL, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_UL);
|
||||
Line line1 = line.createChild(matcher.group(1));
|
||||
line.setAttr(0);
|
||||
Line parent = line.parentLine();
|
||||
LineQueue queue = queueProvider.getQueue();
|
||||
Line prev = line.prevLine();
|
||||
boolean inQuota = queue.currLine().getType() == Line.LINE_TYPE_QUOTA;
|
||||
if (inQuota) {
|
||||
line.setHandle(Line.HANDLE_BY_ROOT);
|
||||
line.setData(Line.LINE_TYPE_UL);
|
||||
}
|
||||
if (prev != null && (prev.getType() == Line.LINE_TYPE_OL || prev.getType() == Line.LINE_TYPE_UL)) {
|
||||
if (level > 0) {
|
||||
line.setAttr(level);
|
||||
} else {
|
||||
String m = line.getSource().substring(matcher.start(), matcher.start(1) - 2);
|
||||
m = m.replaceAll("\\t", " ");
|
||||
if (m.length() > prev.getAttr() * 2 + 1)
|
||||
line.setAttr(prev.getAttr() + 1);
|
||||
else
|
||||
line.setAttr(m.length() / 2);
|
||||
}
|
||||
|
||||
}
|
||||
if (inQuota) {
|
||||
line.setStyle(" ");
|
||||
} else {
|
||||
line.setStyle(styleBuilder.ul(" ", line.getAttr()));
|
||||
}
|
||||
if (find(Tag.UL, line1)) {
|
||||
int nextLevel = line.getAttr() + 1;
|
||||
line1.unAttachFromParent();
|
||||
|
||||
if (parent != null) {
|
||||
Line p = parent.copyToNext();
|
||||
p.addChild(line1);
|
||||
queue.next();
|
||||
ul(line1, nextLevel);
|
||||
if (inQuota) {
|
||||
while (p.parentLine() != null) {
|
||||
p = p.parentLine();
|
||||
}
|
||||
p.setStyle(styleBuilder.ul2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr()));
|
||||
} else {
|
||||
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
|
||||
p.setStyle(styleBuilder.quota(line1.getStyle()));
|
||||
p = p.parentLine();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
line.addNext(line1);
|
||||
queue.next();
|
||||
ul(queue.currLine(), nextLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (find(Tag.OL, line1)) {
|
||||
int nextLevel = line.getAttr() + 1;
|
||||
line1.unAttachFromParent();
|
||||
|
||||
if (parent != null) {
|
||||
Line p = parent.copyToNext();
|
||||
p.addChild(line1);
|
||||
queue.next();
|
||||
ol(line1, nextLevel);
|
||||
if (inQuota) {
|
||||
while (p.parentLine() != null) {
|
||||
p = p.parentLine();
|
||||
}
|
||||
p.setStyle(styleBuilder.ol2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr(), line1.getCount()));
|
||||
} else {
|
||||
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
|
||||
p.setStyle(styleBuilder.quota(line1.getStyle()));
|
||||
p = p.parentLine();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
line.addNext(line1);
|
||||
queue.next();
|
||||
ol(queue.currLine(), nextLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CharSequence userText;
|
||||
if (h(line1)) {
|
||||
userText = line1.getStyle();
|
||||
} else {
|
||||
userText = line1.getSource();
|
||||
}
|
||||
SpannableStringBuilder builder;
|
||||
if (userText instanceof SpannableStringBuilder) {
|
||||
builder = (SpannableStringBuilder) userText;
|
||||
} else {
|
||||
builder = new SpannableStringBuilder(userText);
|
||||
}
|
||||
line.setStyle(builder);
|
||||
inline(line);
|
||||
if (!inQuota) {
|
||||
line.setStyle(styleBuilder.ul(line.getStyle(), line.getAttr()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ol(Line line) {
|
||||
return ol(line, 0);
|
||||
}
|
||||
|
||||
private boolean ol(Line line, int level) {
|
||||
if (line.getSource() != null && (line.getSource().startsWith("- [ ]") || line.getSource().startsWith("- [x]"))) {
|
||||
if (line.getSource().startsWith("- [x]")) {
|
||||
return todoChecked(line);
|
||||
} else {
|
||||
return todoUnChecked(line);
|
||||
}
|
||||
}
|
||||
Matcher matcher = obtain(Tag.OL, line.getSource());
|
||||
if (matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_OL);
|
||||
Line line1 = new Line(matcher.group(1));
|
||||
line.setAttr(0);
|
||||
|
||||
Line parent = line.parentLine();
|
||||
LineQueue queue = queueProvider.getQueue();
|
||||
Line prev = line.prevLine();
|
||||
boolean inQuota = queue.currLine().getType() == Line.LINE_TYPE_QUOTA;
|
||||
if (inQuota) {
|
||||
line.setHandle(Line.HANDLE_BY_ROOT);
|
||||
line.setData(Line.LINE_TYPE_OL);
|
||||
}
|
||||
|
||||
if (prev != null && (prev.getType() == Line.LINE_TYPE_OL || prev.getType() == Line.LINE_TYPE_UL)) {
|
||||
if (level > 0) {
|
||||
line.setAttr(level);
|
||||
} else {
|
||||
String m = line.getSource().substring(matcher.start(), matcher.start(1) - 2);
|
||||
m = m.replaceAll("\\t", " ");
|
||||
if (m.length() > prev.getAttr() * 2 + 1)
|
||||
line.setAttr(prev.getAttr() + 1);
|
||||
else
|
||||
line.setAttr(m.length() / 2);
|
||||
}
|
||||
|
||||
}
|
||||
if (prev != null && prev.getType() == Line.LINE_TYPE_OL && prev.getAttr() == line.getAttr()) {
|
||||
line.setCount(prev.getCount() + 1);
|
||||
} else {
|
||||
line.setCount(1);
|
||||
}
|
||||
if (inQuota) {
|
||||
line.setStyle(" ");
|
||||
} else {
|
||||
line.setStyle(styleBuilder.ol(" ", line.getAttr(), line.getCount()));
|
||||
}
|
||||
if (find(Tag.UL, line1)) {
|
||||
int nextLevel = line.getAttr() + 1;
|
||||
line1.unAttachFromParent();
|
||||
|
||||
if (parent != null) {
|
||||
Line p = parent.copyToNext();
|
||||
p.addChild(line1);
|
||||
queue.next();
|
||||
ul(line1, nextLevel);
|
||||
if (inQuota) {
|
||||
while (p.parentLine() != null) {
|
||||
p = p.parentLine();
|
||||
}
|
||||
p.setStyle(styleBuilder.ul2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr()));
|
||||
} else {
|
||||
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
|
||||
p.setStyle(styleBuilder.quota(line1.getStyle()));
|
||||
p = p.parentLine();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
line.addNext(line1);
|
||||
queue.next();
|
||||
ul(queue.currLine(), nextLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (find(Tag.OL, line1)) {
|
||||
int nextLevel = line.getAttr() + 1;
|
||||
line1.unAttachFromParent();
|
||||
|
||||
if (parent != null) {
|
||||
Line p = parent.copyToNext();
|
||||
p.addChild(line1);
|
||||
queue.next();
|
||||
ol(line1, nextLevel);
|
||||
if (inQuota) {
|
||||
while (p.parentLine() != null) {
|
||||
p = p.parentLine();
|
||||
}
|
||||
p.setStyle(styleBuilder.ol2(line1.getStyle(), findCount(Tag.QUOTA, p, 1) - 1, line1.getAttr(), line1.getCount()));
|
||||
} else {
|
||||
while (p != null && p.getType() == Line.LINE_TYPE_QUOTA) {
|
||||
p.setStyle(styleBuilder.quota(line1.getStyle()));
|
||||
p = p.parentLine();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
line.addNext(line1);
|
||||
queue.next();
|
||||
ol(queue.currLine(), nextLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CharSequence userText;
|
||||
if (h(line1)) {
|
||||
userText = line1.getStyle();
|
||||
} else {
|
||||
userText = line1.getSource();
|
||||
}
|
||||
SpannableStringBuilder builder;
|
||||
if (userText instanceof SpannableStringBuilder) {
|
||||
builder = (SpannableStringBuilder) userText;
|
||||
} else {
|
||||
builder = new SpannableStringBuilder(userText);
|
||||
}
|
||||
line.setStyle(builder);
|
||||
inline(line);
|
||||
|
||||
if (!inQuota) {
|
||||
line.setStyle(styleBuilder.ol(line.getStyle(), line.getAttr(), line.getCount()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean gap(Line line) {
|
||||
line = line.get();
|
||||
Matcher matcher = obtain(Tag.GAP, line.getSource());
|
||||
if (matcher.matches()) {
|
||||
line.setType(Line.LINE_TYPE_GAP);
|
||||
line.setStyle(styleBuilder.gap());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean em(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.EM, builder);
|
||||
while (matcher.find()) {
|
||||
int start = matcher.start(1);
|
||||
int end = matcher.end(1);
|
||||
if (checkInCode(builder, start, end)) {
|
||||
continue;
|
||||
}
|
||||
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.em(sb));
|
||||
em(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean italic(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.ITALIC, builder);
|
||||
while (matcher.find()) {
|
||||
int start = matcher.start(1);
|
||||
int end = matcher.end(1);
|
||||
if (checkInCode(builder, start, end)) {
|
||||
continue;
|
||||
}
|
||||
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.italic(sb));
|
||||
italic(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emItalic(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.EM_ITALIC, builder);
|
||||
while (matcher.find()) {
|
||||
int start = matcher.start(1);
|
||||
int end = matcher.end(1);
|
||||
if (checkInCode(builder, start, end)) {
|
||||
continue;
|
||||
}
|
||||
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.emItalic(sb));
|
||||
emItalic(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean code(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
if (builder.toString().endsWith("```") || builder.toString().startsWith("```")) {
|
||||
return codeBlock2(line);
|
||||
}
|
||||
Matcher matcher = obtain(Tag.CODE, builder);
|
||||
if (matcher.find()) {
|
||||
String content = matcher.group(3);
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.code(content));
|
||||
code(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean email(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.EMAIL, builder);
|
||||
if (matcher.find()) {
|
||||
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(2), matcher.end(2));
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.email(sb));
|
||||
email(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.DELETE, builder);
|
||||
while (matcher.find()) {
|
||||
int start = matcher.start(1);
|
||||
int end = matcher.end(1);
|
||||
if (checkInCode(builder, start, end)) {
|
||||
continue;
|
||||
}
|
||||
SpannableStringBuilder sb = (SpannableStringBuilder) builder.subSequence(matcher.start(3), matcher.end(3));
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.delete(sb));
|
||||
delete(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoLink(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.AUTO_LINK, builder);
|
||||
boolean m = false;
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group();
|
||||
builder.delete(matcher.start(), matcher.end());
|
||||
builder.insert(matcher.start(), styleBuilder.link(content, content, ""));
|
||||
m = true;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean link(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.LINK, builder);
|
||||
if (matcher.find()) {
|
||||
String title = matcher.group(2);
|
||||
String link = matcher.group(3);
|
||||
String hint = matcher.group(6);
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.link(title, link, hint));
|
||||
link(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean link2(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.LINK2, builder);
|
||||
if (matcher.find()) {
|
||||
String title = matcher.group(2);
|
||||
String id = matcher.group(3);
|
||||
Pair<String, String> link = idLinkLinks.get(id);
|
||||
if (link != null) {
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.link(title, link.first, link.second));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
link2(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkId(String line) {
|
||||
Matcher matcher = obtain(Tag.LINK_ID, line);
|
||||
if (matcher.find()) {
|
||||
String id = matcher.group(1);
|
||||
String link = matcher.group(2);
|
||||
String hint = matcher.group(5);
|
||||
idLinkLinks.put(id, new Pair<>(link, hint));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean image(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.IMAGE, builder);
|
||||
if (matcher.find()) {
|
||||
String title = matcher.group(2);
|
||||
String link = matcher.group(3);
|
||||
String hint = matcher.group(6);
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.image(title, link, hint));
|
||||
image(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean image2(Line line) {
|
||||
line = line.get();
|
||||
SpannableStringBuilder builder = (SpannableStringBuilder) line.getStyle();
|
||||
Matcher matcher = obtain(Tag.IMAGE2, builder);
|
||||
if (matcher.find()) {
|
||||
String title = matcher.group(2);
|
||||
String id = matcher.group(3);
|
||||
Pair<String, String> image = idImageUrl.get(id);
|
||||
if (image != null) {
|
||||
builder.delete(matcher.start(1), matcher.end(1));
|
||||
builder.insert(matcher.start(1), styleBuilder.image(title, image.first, image.second));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
image2(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean imageId(String line) {
|
||||
Matcher matcher = obtain(Tag.IMAGE_ID, line);
|
||||
if (matcher.find()) {
|
||||
String id = matcher.group(1);
|
||||
String link = matcher.group(2);
|
||||
String hint = matcher.group(5);
|
||||
idImageUrl.put(id, new Pair<>(link, hint));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean codeBlock1(Line line) {
|
||||
Matcher matcher = obtain(Tag.CODE_BLOCK_1, line.getSource());
|
||||
if (matcher.find()) {
|
||||
String content = matcher.group(2);
|
||||
LineQueue queue = queueProvider.getQueue();
|
||||
Line next = queue.nextLine();
|
||||
StringBuilder sb = new StringBuilder(content);
|
||||
StringBuilder bsb = new StringBuilder();
|
||||
|
||||
while (next != null) {
|
||||
CharSequence r = get(Tag.CODE_BLOCK_1, next, 2);
|
||||
if (r == null) {
|
||||
if (find(Tag.BLANK, next)) {
|
||||
bsb.append(' ').append('\n');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (bsb.length() != 0) {
|
||||
sb.append(bsb).append(r);
|
||||
bsb.delete(0, sb.length());
|
||||
} else {
|
||||
sb.append('\n').append(r);
|
||||
}
|
||||
}
|
||||
queue.removeNextLine();
|
||||
next = queue.nextLine();
|
||||
}
|
||||
|
||||
|
||||
line.setType(Line.LINE_TYPE_CODE_BLOCK_1);
|
||||
line.setStyle(styleBuilder.codeBlock(sb.toString()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean codeBlock2(Line line) {
|
||||
if (find(Tag.CODE_BLOCK_2, line)) {
|
||||
LineQueue queue = queueProvider.getQueue();
|
||||
LineQueue nextQueue = queue.copy();
|
||||
boolean find = false;
|
||||
while (nextQueue.nextLine() != null) {
|
||||
if (find(Tag.CODE_BLOCK_2, nextQueue.nextLine())) {
|
||||
nextQueue.next();
|
||||
removePrevBlankLine(nextQueue);
|
||||
removeNextBlankLine(queue);
|
||||
find = true;
|
||||
break;
|
||||
}
|
||||
nextQueue.next();
|
||||
}
|
||||
if (find) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
queue.next();
|
||||
queue.removePrevLine();
|
||||
while (queue.currLine() != nextQueue.currLine()) {
|
||||
sb.append(queue.currLine().getSource()).append('\n');
|
||||
queue.next();
|
||||
queue.removePrevLine();
|
||||
}
|
||||
removeNextBlankLine(nextQueue);
|
||||
nextQueue.currLine().setType(Line.LINE_TYPE_CODE_BLOCK_2);
|
||||
nextQueue.currLine().setStyle(styleBuilder.codeBlock(sb.toString()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean todoChecked(Line line) {
|
||||
Matcher matcher = obtain(Tag.TODO_CHECKED, line.getSource());
|
||||
if (matcher != null && matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_CHECKED);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.checked(line.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean todoUnChecked(Line line) {
|
||||
Matcher matcher = obtain(Tag.TODO_UNCHECKED, line.getSource());
|
||||
if (matcher != null && matcher.find()) {
|
||||
line.setType(Line.LINE_TYPE_UN_CHECKED);
|
||||
line.setStyle(SpannableStringBuilder.valueOf(matcher.group(1)));
|
||||
inline(line);
|
||||
line.setStyle(styleBuilder.unChecked(line.getStyle()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inline(Line line) {
|
||||
boolean flag = code(line);
|
||||
flag = emItalic(line) || flag;
|
||||
flag = em(line) || flag;
|
||||
flag = italic(line) || flag;
|
||||
flag = delete(line) || flag;
|
||||
flag = email(line) || flag;
|
||||
flag = image(line) || flag;
|
||||
flag = image2(line) || flag;
|
||||
flag = link(line) || flag;
|
||||
flag = link2(line) || flag;
|
||||
flag = autoLink(line) || flag;
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean find(int tag, Line line) {
|
||||
return line != null && find(tag, line.getSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean find(int tag, String line) {
|
||||
if (line == null) {
|
||||
return false;
|
||||
}
|
||||
Matcher m = obtain(tag, line);
|
||||
return m != null && m.find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findCount(int tag, Line line, int group) {
|
||||
return line == null ? 0 : findCount(tag, line.getSource(), group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findCount(int tag, String line, int group) {
|
||||
if (line == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Matcher matcher = obtain(tag, line);
|
||||
if (matcher == null) {
|
||||
return 0;
|
||||
}
|
||||
if (matcher.find()) {
|
||||
return findCount(tag, matcher.group(group), group) + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean checkInCode(SpannableStringBuilder builder, int start, int end) {
|
||||
CodeSpan[] css = builder.getSpans(0, builder.length(), CodeSpan.class);
|
||||
for (CodeSpan cs : css) {
|
||||
int c_start = builder.getSpanStart(cs);
|
||||
int c_end = builder.getSpanEnd(cs);
|
||||
if (!(c_start >= end || c_end <= start)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Matcher obtain(int tag, CharSequence src) {
|
||||
Matcher m = matchers.get(tag, null);
|
||||
if (m != null) {
|
||||
m.reset(src);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
public void setQueueProvider(QueueProvider queueProvider) {
|
||||
this.queueProvider = queueProvider;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence get(int tag, Line line, int group) {
|
||||
return get(tag, line.getSource(), group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence get(int tag, CharSequence line, int group) {
|
||||
Matcher m = obtain(tag, line);
|
||||
return m.find() ? m.group(group) : null;
|
||||
}
|
||||
|
||||
private void removeNextBlankLine(LineQueue queue) {
|
||||
while (queue.nextLine() != null) {
|
||||
if (find(Tag.BLANK, queue.nextLine())) {
|
||||
queue.removeNextLine();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removePrevBlankLine(LineQueue queue) {
|
||||
while (queue.prevLine() != null) {
|
||||
if (find(Tag.BLANK, queue.prevLine())) {
|
||||
queue.removePrevLine();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.style.LineHeightSpan;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-2.
|
||||
* 代码块Span
|
||||
*/
|
||||
public class CodeBlockSpan extends ReplacementSpan implements LineHeightSpan {
|
||||
|
||||
private static final float radius = 10;
|
||||
private static final int padding = 30;
|
||||
|
||||
private int width;
|
||||
private Drawable drawable;
|
||||
private int baseLine;
|
||||
private int lineHeight;
|
||||
private CharSequence[] ls;
|
||||
private List<Pair<Integer, Integer>> lines;
|
||||
|
||||
public CodeBlockSpan(int width, int color, CharSequence... lines) {
|
||||
this.width = width;
|
||||
GradientDrawable g = new GradientDrawable();
|
||||
g.setColor(color);
|
||||
g.setCornerRadius(radius);
|
||||
drawable = g;
|
||||
this.ls = lines;
|
||||
}
|
||||
|
||||
@Override public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
|
||||
if (fm != null && lines == null) {
|
||||
lines = new ArrayList<>();
|
||||
for (CharSequence c : ls) {
|
||||
lines.addAll(measureTextLine(c, 0, c.length(), paint));
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x,
|
||||
int top, int y, int bottom, @NonNull Paint paint) {
|
||||
drawable.setBounds((int) x, top, (int) x + width, bottom);
|
||||
drawable.draw(canvas);
|
||||
int lineNum = 0;
|
||||
x = x + padding;
|
||||
int i = baseLine + lineHeight / 2 + top;
|
||||
for (Pair<Integer, Integer> line : lines) {
|
||||
try {
|
||||
CharSequence t = ls[lineNum];
|
||||
canvas.drawText(t, line.first, line.second, x + padding, i, paint);
|
||||
if (line.second >= t.length()) {
|
||||
lineNum++;
|
||||
}
|
||||
i += lineHeight;
|
||||
} catch (IndexOutOfBoundsException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private int getTextInLineLen(CharSequence text, int start, int end, Paint paint) {
|
||||
int e = start;
|
||||
while (paint.measureText(text, start, e) < width - padding * 2) {
|
||||
e++;
|
||||
if (e > end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return e - 1;
|
||||
}
|
||||
|
||||
private int getTextInLineLenInRange(CharSequence text, int start, int end, int rs, int re, Paint paint) {
|
||||
int e = rs;
|
||||
if (rs > end) {
|
||||
return end;
|
||||
}
|
||||
try {
|
||||
while (paint.measureText(text, start, e) < width - padding * 2) {
|
||||
e++;
|
||||
if (e > end || e > re) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return e - 1;
|
||||
}
|
||||
|
||||
private List<Pair<Integer, Integer>> measureTextLine(CharSequence text, int start, int end, Paint paint) {
|
||||
List<Pair<Integer, Integer>> lines = new ArrayList<>();
|
||||
int l = getTextInLineLen(text, start, end, paint);
|
||||
int count = l;
|
||||
lines.add(new Pair<>(start, l));
|
||||
while (l < end) {
|
||||
int temp = l;
|
||||
l = getTextInLineLenInRange(text, l, end, l + count - 4, l + count + 4, paint);
|
||||
count = l - temp;
|
||||
lines.add(new Pair<>(temp, l));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
@Override public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
|
||||
int num = lines.size();
|
||||
lineHeight = fm.bottom - fm.top;
|
||||
baseLine = -fm.top;
|
||||
fm.ascent = fm.top;
|
||||
fm.bottom += num * lineHeight;
|
||||
fm.descent = fm.bottom;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.URLSpan;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-30.
|
||||
* EmailSpan
|
||||
*/
|
||||
@SuppressLint("ParcelCreator")
|
||||
public class EmailSpan extends URLSpan {
|
||||
|
||||
private int color;
|
||||
|
||||
public EmailSpan(String email, int color) {
|
||||
super(email);
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setColor(color);
|
||||
ds.setUnderlineText(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse("mailto:" + getURL()));
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, "");
|
||||
try {
|
||||
widget.getContext().startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,139 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zzhoujay.markdown.util.NumberKit;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-6-25.
|
||||
* 列表Span
|
||||
*/
|
||||
public class MarkDownBulletSpan extends BulletSpan {
|
||||
private static final int tab = 40;
|
||||
private static final int mGapWidth = 40;
|
||||
private static final int BULLET_RADIUS = 6;
|
||||
|
||||
private final boolean mWantColor;
|
||||
private final int mColor;
|
||||
private final String index;
|
||||
private int level = 0;
|
||||
private int margin;
|
||||
|
||||
private static Path circleBulletPath = null;
|
||||
private static Path rectBulletPath = null;
|
||||
|
||||
private WeakReference<TextView> textViewWeakReference;
|
||||
|
||||
public MarkDownBulletSpan(int l, int color, int pointIndex, TextView textView) {
|
||||
super(mGapWidth, color);
|
||||
level = l;
|
||||
if (pointIndex > 0) {
|
||||
if (level == 1) {
|
||||
this.index = NumberKit.toRomanNumerals(pointIndex);
|
||||
} else if (level >= 2) {
|
||||
this.index = NumberKit.toABC(pointIndex - 1);
|
||||
} else {
|
||||
this.index = pointIndex + "";
|
||||
}
|
||||
} else {
|
||||
index = null;
|
||||
}
|
||||
mWantColor = true;
|
||||
mColor = color;
|
||||
textViewWeakReference = new WeakReference<>(textView);
|
||||
}
|
||||
|
||||
public MarkDownBulletSpan(int level, int color, int pointIndex) {
|
||||
super(mGapWidth, color);
|
||||
this.level = level;
|
||||
if (pointIndex > 0) {
|
||||
if (level == 1) {
|
||||
this.index = NumberKit.toRomanNumerals(pointIndex);
|
||||
} else if (level >= 2) {
|
||||
this.index = NumberKit.toABC(pointIndex - 1);
|
||||
} else {
|
||||
this.index = pointIndex + "";
|
||||
}
|
||||
} else {
|
||||
index = null;
|
||||
}
|
||||
mWantColor = true;
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getLeadingMargin(boolean first) {
|
||||
TextView textView = textViewWeakReference != null ? textViewWeakReference.get() : null;
|
||||
if (index != null && textView != null) {
|
||||
margin = (int) (tab + (mGapWidth + textView.getPaint().measureText(index)) * (level + 1));
|
||||
} else {
|
||||
margin = (2 * BULLET_RADIUS + mGapWidth) * (level + 1) + tab;
|
||||
}
|
||||
return margin;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
@Override
|
||||
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout l) {
|
||||
if (((Spanned) text).getSpanStart(this) == start) {
|
||||
int oldcolor = 0;
|
||||
if (mWantColor) {
|
||||
oldcolor = p.getColor();
|
||||
p.setColor(mColor);
|
||||
}
|
||||
if (index != null) {
|
||||
c.drawText(index + '.', x - p.measureText(index) + margin - mGapWidth, baseline, p);
|
||||
} else {
|
||||
Paint.Style style = p.getStyle();
|
||||
if (level == 1) {
|
||||
p.setStyle(Paint.Style.STROKE);
|
||||
} else {
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
if (c.isHardwareAccelerated()) {
|
||||
Path path;
|
||||
if (level >= 2) {
|
||||
if (rectBulletPath == null) {
|
||||
rectBulletPath = new Path();
|
||||
float w = 1.2f * BULLET_RADIUS;
|
||||
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
|
||||
}
|
||||
path = rectBulletPath;
|
||||
} else {
|
||||
if (circleBulletPath == null) {
|
||||
circleBulletPath = new Path();
|
||||
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
|
||||
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
|
||||
}
|
||||
path = circleBulletPath;
|
||||
}
|
||||
|
||||
c.save();
|
||||
c.translate(x + margin - mGapWidth, (top + bottom) / 2.0f);
|
||||
c.drawPath(path, p);
|
||||
c.restore();
|
||||
} else {
|
||||
c.drawCircle(x + margin - mGapWidth, (top + bottom) / 2.0f, BULLET_RADIUS, p);
|
||||
}
|
||||
|
||||
p.setStyle(style);
|
||||
}
|
||||
if (mWantColor) {
|
||||
p.setColor(oldcolor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.os.Build;
|
||||
import android.text.style.ReplacementSpan;
|
||||
|
||||
import com.zzhoujay.markdown.util.NumberKit;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-3.
|
||||
* 列表Span
|
||||
*/
|
||||
public class MarkDownInnerBulletSpan extends ReplacementSpan {
|
||||
|
||||
private static final int BULLET_RADIUS = 6;
|
||||
private static final int tab = 40;
|
||||
private static final int gap = 40;
|
||||
|
||||
private final int mColor;
|
||||
private final String index;
|
||||
private int margin;
|
||||
private int level;
|
||||
|
||||
private static Path circleBulletPath = null;
|
||||
private static Path rectBulletPath = null;
|
||||
|
||||
|
||||
public MarkDownInnerBulletSpan(int level, int mColor, int index) {
|
||||
this.mColor = mColor;
|
||||
this.level = level;
|
||||
if (index > 0) {
|
||||
if (level == 1) {
|
||||
this.index = NumberKit.toRomanNumerals(index) + '.';
|
||||
} else if (level >= 2) {
|
||||
this.index = NumberKit.toABC(index - 1) + '.';
|
||||
} else {
|
||||
this.index = index + ".";
|
||||
}
|
||||
} else {
|
||||
this.index = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
|
||||
if (index == null) {
|
||||
margin = tab + (gap + BULLET_RADIUS * 2) * (level + 1);
|
||||
} else {
|
||||
margin = (int) (tab + (gap + paint.measureText(index)) * (level + 1));
|
||||
}
|
||||
return (int) (margin + paint.measureText(text, start, end));
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
@Override
|
||||
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
|
||||
int oldcolor = paint.getColor();
|
||||
paint.setColor(mColor);
|
||||
// draw bullet
|
||||
if (index != null) {
|
||||
canvas.drawText(index, x + tab, y, paint);
|
||||
} else {
|
||||
Paint.Style style = paint.getStyle();
|
||||
|
||||
if (level == 1) {
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
} else {
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
if (canvas.isHardwareAccelerated()) {
|
||||
Path path;
|
||||
if (level >= 2) {
|
||||
if (rectBulletPath == null) {
|
||||
rectBulletPath = new Path();
|
||||
float w = 1.2f * BULLET_RADIUS;
|
||||
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
|
||||
}
|
||||
path = rectBulletPath;
|
||||
} else {
|
||||
if (circleBulletPath == null) {
|
||||
circleBulletPath = new Path();
|
||||
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
|
||||
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
|
||||
}
|
||||
path = circleBulletPath;
|
||||
}
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(x + margin - gap, (top + bottom) / 2.0f);
|
||||
canvas.drawPath(path, paint);
|
||||
canvas.restore();
|
||||
} else {
|
||||
canvas.drawCircle(x + margin - gap, (top + bottom) / 2.0f, BULLET_RADIUS, paint);
|
||||
}
|
||||
paint.setStyle(style);
|
||||
}
|
||||
// drawText
|
||||
canvas.drawText(text, start, end, x + margin, y, paint);
|
||||
paint.setColor(oldcolor);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zzhoujay.markdown.util.NumberKit;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-30.
|
||||
*/
|
||||
public class QuotaBulletSpan extends QuoteSpan {
|
||||
|
||||
private static final int tab = 40;
|
||||
private static final int mGapWidth = 40;
|
||||
private static final int BULLET_RADIUS = 6;
|
||||
|
||||
|
||||
private static final int STRIPE_WIDTH = 15;
|
||||
private static final int GAP_WIDTH = 40;
|
||||
|
||||
private static Path circleBulletPath = null;
|
||||
private static Path rectBulletPath = null;
|
||||
|
||||
private final String index;
|
||||
private int level = 0;
|
||||
private int bulletColor;
|
||||
private int margin;
|
||||
private WeakReference<TextView> textViewWeakReference;
|
||||
private int quotaLevel;
|
||||
|
||||
|
||||
public QuotaBulletSpan(int quotaLevel, int bulletLevel, int quotaColor, int bulletColor, int pointIndex, TextView textView) {
|
||||
super(quotaColor);
|
||||
this.quotaLevel = quotaLevel;
|
||||
this.level = bulletLevel;
|
||||
if (pointIndex > 0) {
|
||||
if (bulletLevel == 1) {
|
||||
this.index = NumberKit.toRomanNumerals(pointIndex);
|
||||
} else if (bulletLevel >= 2) {
|
||||
this.index = NumberKit.toABC(pointIndex - 1);
|
||||
} else {
|
||||
this.index = pointIndex + "";
|
||||
}
|
||||
} else {
|
||||
index = null;
|
||||
}
|
||||
this.bulletColor = bulletColor;
|
||||
this.textViewWeakReference = new WeakReference<>(textView);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
@Override
|
||||
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
|
||||
// draw quota
|
||||
Paint.Style style = p.getStyle();
|
||||
int color = p.getColor();
|
||||
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
p.setColor(getColor());
|
||||
|
||||
int i = 0;
|
||||
int quotaWidth = STRIPE_WIDTH + GAP_WIDTH;
|
||||
|
||||
while (i <= quotaLevel) {
|
||||
int offset = i * quotaWidth;
|
||||
c.drawRect(x + offset, top, x + offset + dir * STRIPE_WIDTH, bottom, p);
|
||||
i++;
|
||||
}
|
||||
|
||||
p.setStyle(style);
|
||||
p.setColor(color);
|
||||
|
||||
// draw bullet
|
||||
if (((Spanned) text).getSpanStart(this) == start) {
|
||||
int oldColor;
|
||||
oldColor = p.getColor();
|
||||
p.setColor(bulletColor);
|
||||
if (index != null) {
|
||||
c.drawText(index + '.', x - p.measureText(index) + margin - mGapWidth, baseline, p);
|
||||
} else {
|
||||
style = p.getStyle();
|
||||
if (level == 1) {
|
||||
p.setStyle(Paint.Style.STROKE);
|
||||
} else {
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
if (c.isHardwareAccelerated()) {
|
||||
Path path;
|
||||
if (level >= 2) {
|
||||
if (rectBulletPath == null) {
|
||||
rectBulletPath = new Path();
|
||||
float w = 1.2f * BULLET_RADIUS;
|
||||
rectBulletPath.addRect(-w, -w, w, w, Path.Direction.CW);
|
||||
}
|
||||
path = rectBulletPath;
|
||||
} else {
|
||||
if (circleBulletPath == null) {
|
||||
circleBulletPath = new Path();
|
||||
// Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
|
||||
circleBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Path.Direction.CW);
|
||||
}
|
||||
path = circleBulletPath;
|
||||
}
|
||||
|
||||
c.save();
|
||||
c.translate(x + margin - mGapWidth, (top + bottom) / 2.0f);
|
||||
c.drawPath(path, p);
|
||||
c.restore();
|
||||
} else {
|
||||
c.drawCircle(x + margin - mGapWidth, (top + bottom) / 2.0f, BULLET_RADIUS, p);
|
||||
}
|
||||
|
||||
p.setStyle(style);
|
||||
}
|
||||
p.setColor(oldColor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLeadingMargin(boolean first) {
|
||||
if (textViewWeakReference == null && margin != 0) {
|
||||
return margin;
|
||||
}
|
||||
TextView textView = textViewWeakReference.get();
|
||||
if (index != null && textView != null) {
|
||||
margin = (int) (tab + (mGapWidth + textView.getPaint().measureText(index)) * (level + 1));
|
||||
} else {
|
||||
margin = (2 * BULLET_RADIUS + mGapWidth) * (level + 1) + tab;
|
||||
}
|
||||
int bulletMargin = (quotaLevel + 1) * (STRIPE_WIDTH + GAP_WIDTH);
|
||||
margin += bulletMargin;
|
||||
return margin;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.text.style.LineHeightSpan;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-2.
|
||||
* ScaleHeightSpan
|
||||
*/
|
||||
public class ScaleHeightSpan implements LineHeightSpan {
|
||||
|
||||
private float scale;
|
||||
|
||||
public ScaleHeightSpan(float scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
|
||||
fm.ascent *= scale;
|
||||
fm.top *= scale;
|
||||
fm.descent *= scale;
|
||||
fm.bottom *= scale;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package com.zzhoujay.markdown.style;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.style.LineHeightSpan;
|
||||
import android.text.style.ReplacementSpan;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-2.
|
||||
*/
|
||||
public class UnderLineSpan extends ReplacementSpan implements LineHeightSpan {
|
||||
|
||||
private int height;
|
||||
private int width;
|
||||
private Drawable drawable;
|
||||
|
||||
public UnderLineSpan(Drawable drawable, int width, int height) {
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.drawable = drawable;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
|
||||
return (int) paint.measureText(text, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
|
||||
drawable.setBounds((int) x, bottom - height, (int) x + width, bottom);
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
|
||||
fm.top /= 3;
|
||||
fm.ascent /= 3;
|
||||
fm.bottom /= 3;
|
||||
fm.descent /= 3;
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package com.zzhoujay.markdown.util;
|
||||
|
||||
import android.graphics.Paint;
|
||||
|
||||
/**
|
||||
* Created by zhou on 16-7-3.
|
||||
*/
|
||||
public class FontKit {
|
||||
|
||||
public static void scale(Paint.FontMetricsInt fm, float scale) {
|
||||
fm.top *= scale;
|
||||
fm.bottom *= scale;
|
||||
fm.ascent *= scale;
|
||||
fm.descent *= scale;
|
||||
fm.leading *= scale;
|
||||
}
|
||||
|
||||
public static void scaleTo(Paint.FontMetricsInt from, Paint.FontMetricsInt to, float scale) {
|
||||
to.top = (int) (from.top * scale);
|
||||
to.bottom = (int) (from.bottom * scale);
|
||||
to.ascent = (int) (from.ascent * scale);
|
||||
to.descent = (int) (from.descent * scale);
|
||||
to.leading = (int) (from.leading * scale);
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
package com.zzhoujay.markdown.util;
|
||||
|
||||
/**
|
||||
* created by zhou on 16-7-17.
|
||||
*/
|
||||
public class NumberKit {
|
||||
|
||||
private static final String[] digit = {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
|
||||
private static final String[] ten = {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
|
||||
private static final String[] hundreds = {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
|
||||
private static final String[] thousand = {"", "m", "mm", "mmm"};
|
||||
|
||||
private static final int ROMAN_MAX = 4996;
|
||||
|
||||
public static String toRomanNumerals(int num) {
|
||||
while (num > ROMAN_MAX) {
|
||||
num -= ROMAN_MAX;
|
||||
}
|
||||
String th = thousand[num / 1000];
|
||||
num %= 1000;
|
||||
String hu = hundreds[num / 100];
|
||||
num %= 100;
|
||||
String te = ten[num / 10];
|
||||
num %= 10;
|
||||
String di = digit[num];
|
||||
return String.format("%s%s%s%s", th, hu, te, di);
|
||||
}
|
||||
|
||||
public static String toABC(int num) {
|
||||
int a = num / 26;
|
||||
int b = num % 26;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (a > 26) {
|
||||
sb.append(toABC(a - 1)).append((char) (b + 'a'));
|
||||
} else if (a == 0) {
|
||||
sb.append((char) (b + 'a'));
|
||||
} else {
|
||||
sb.append((char) (a + 'a')).append((char) (b + 'a'));
|
||||
}
|
||||
return sb.toString();
|
||||
// while (num < 0) {
|
||||
// num += 26;
|
||||
// }
|
||||
// while (num > 26) {
|
||||
// num -= 26;
|
||||
// }
|
||||
// return String.valueOf((char) (num + 'a'));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user