Skip to content

Commit 1357760

Browse files
authored
Merge pull request #2 from SysdataSpA/develop
update spanners and css compilers
2 parents e4918bc + c37c333 commit 1357760

14 files changed

Lines changed: 187 additions & 357 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Android HTML rendering library
2+
3+
HtmlSpanner started as the HTML rendering library for PageTurner, but looking through some questions on StackOverflow I noticed how many people were struggling with the infamous ``Html.fromHtml()`` and getting its results to display properly in TextViews.
4+
5+
HtmlSpanner allows you full control over how tags are rendered and gives you all the data about the location of a tag in the text. This allows proper handling of anchors, tables, numbered lists and unordered lists.
6+
7+
The default link implementation just opens URLs, but it can be easily overridden to support anchors.
8+
9+
HtmlSpanner uses HtmlCleaner to do most of the heavy lifting for parsing HTML files.
10+
11+
# CSS support
12+
13+
HtmlSpanner now also supports the most common subset of CSS: both style tags and style attributes are parsed by default, and the style of all built-in tags can be updated.
14+
15+
# Usage
16+
In its simplest form, just call ``(new HtmlSpanner()).fromHtml()`` to get similar output as Android's ``Html.fromHtml()``.
17+
18+
# HTMLCleaner Source
19+
see http://htmlcleaner.sourceforge.net/index.php
20+
21+
The fork under https://github.com/amplafi/htmlcleaner can not be used on Android <= 2.1 as it uses java.lang.String.isEmpty

html-textview-master/HtmlSpanner/build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ repositories {
1919
dependencies {
2020
compile 'net.sourceforge.htmlcleaner:htmlcleaner:2.16'
2121
compile 'com.osbcp.cssparser:cssparser:1.5'
22+
compile 'io.reactivex:rxjava:1.2.10'
23+
compile 'io.reactivex:rxandroid:1.2.1'
2224
}
2325

2426
android {
25-
compileSdkVersion 19
26-
buildToolsVersion "19.1.0"
27+
compileSdkVersion 21
28+
buildToolsVersion "21.1.0"
2729

2830
defaultConfig {
2931
minSdkVersion 9
30-
targetSdkVersion 19
32+
targetSdkVersion 21
3133
}
3234
}
3335

html-textview-master/HtmlSpanner/src/main/java/net/nightwhistler/htmlspanner/HtmlSpanner.java

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import java.io.InputStream;
2121
import java.io.Reader;
2222
import java.util.HashMap;
23+
import java.util.LinkedHashMap;
2324
import java.util.Map;
2425

2526
import android.graphics.Color;
27+
import android.text.TextUtils;
2628
import android.util.Log;
2729
import net.nightwhistler.htmlspanner.exception.ParsingCancelledException;
2830
import net.nightwhistler.htmlspanner.handlers.*;
@@ -70,6 +72,11 @@ public class HtmlSpanner {
7072
private FontResolver fontResolver;
7173

7274
private int backgroundColor;
75+
76+
private int textColor;
77+
78+
private float textSize;
79+
7380
/**
7481
* Switch to determine if CSS is used
7582
*/
@@ -79,13 +86,70 @@ public class HtmlSpanner {
7986
* If CSS colours are used
8087
*/
8188
private boolean useColoursFromStyle = true;
89+
private static Map<String, String> htmlTagsDictionary;
90+
91+
static {
92+
htmlTagsDictionary = new LinkedHashMap<>();
93+
htmlTagsDictionary.put("\r\n", "\n");
94+
htmlTagsDictionary.put("\r", "\n");
95+
htmlTagsDictionary.put("\n","<br>");
96+
htmlTagsDictionary.put("&gt;", ">");
97+
htmlTagsDictionary.put("&lt;", "<");
98+
htmlTagsDictionary.put("&bull;", "•");
99+
htmlTagsDictionary.put("&#39;", "'");
100+
htmlTagsDictionary.put("&euro;", "€");
101+
htmlTagsDictionary.put("&#36;", "$");
102+
htmlTagsDictionary.put("&nbsp;", " ");
103+
htmlTagsDictionary.put("&rsquo;", "'");
104+
htmlTagsDictionary.put("&lsquo;", "'");
105+
htmlTagsDictionary.put("&ldquo;", "\"");
106+
htmlTagsDictionary.put("&rdquo;", "\"");
107+
htmlTagsDictionary.put("&ndash;", "-");
108+
htmlTagsDictionary.put("&#95;", "_");
109+
htmlTagsDictionary.put("&copy;", "&#169;");
110+
htmlTagsDictionary.put("&divide;", "&#247;");
111+
htmlTagsDictionary.put("&micro;", "&#181;");
112+
htmlTagsDictionary.put("&middot;", "&#183;");
113+
htmlTagsDictionary.put("&para;", "&#182;");
114+
htmlTagsDictionary.put("&plusmn;", "&#177;");
115+
htmlTagsDictionary.put("&reg;", "&#174;");
116+
htmlTagsDictionary.put("&sect;", "&#167;");
117+
htmlTagsDictionary.put("&trade;", "&#153;");
118+
htmlTagsDictionary.put("&yen;", "&#165;");
119+
htmlTagsDictionary.put("&pound;", "£");
120+
htmlTagsDictionary.put("&raquo;", ">>");
121+
htmlTagsDictionary.put("&laquo;", "<<");
122+
htmlTagsDictionary.put("&hellip;", "...");
123+
htmlTagsDictionary.put("&agrave;", "à");
124+
htmlTagsDictionary.put("&egrave;", "è");
125+
htmlTagsDictionary.put("&igrave;", "ì");
126+
htmlTagsDictionary.put("&ograve;", "ò");
127+
htmlTagsDictionary.put("&ugrave;", "ù");
128+
htmlTagsDictionary.put("&aacute;", "á");
129+
htmlTagsDictionary.put("&eacute;", "é");
130+
htmlTagsDictionary.put("&iacute;", "í");
131+
htmlTagsDictionary.put("&oacute;", "ó");
132+
htmlTagsDictionary.put("&uacute;", "ú");
133+
htmlTagsDictionary.put("&Agrave;", "À");
134+
htmlTagsDictionary.put("&Egrave;", "È");
135+
htmlTagsDictionary.put("&Igrave;", "Ì");
136+
htmlTagsDictionary.put("&Ograve;", "Ò");
137+
htmlTagsDictionary.put("&Ugrave;", "Ù");
138+
htmlTagsDictionary.put("&Aacute;", "Á");
139+
htmlTagsDictionary.put("&Eacute;", "É");
140+
htmlTagsDictionary.put("&Iacute;", "Í");
141+
htmlTagsDictionary.put("&Oacute;", "Ó");
142+
htmlTagsDictionary.put("&Uacute;", "Ú");
143+
htmlTagsDictionary.put("<h1>","<h1 style=\"font-weight:bold\">");
144+
htmlTagsDictionary.put("<h2>","<h2 style=\"font-weight:bold\">");
145+
}
82146

83147

84148
/**
85149
* Creates a new HtmlSpanner using a default HtmlCleaner instance.
86150
*/
87-
public HtmlSpanner() {
88-
this(createHtmlCleaner(), new SystemFontResolver());
151+
public HtmlSpanner(int textColor,float textSize) {
152+
this(createHtmlCleaner(), new SystemFontResolver(),textColor,textSize);
89153
}
90154

91155
/**
@@ -95,11 +159,12 @@ public HtmlSpanner() {
95159
*
96160
* @param cleaner
97161
*/
98-
public HtmlSpanner(HtmlCleaner cleaner, FontResolver fontResolver) {
162+
public HtmlSpanner(HtmlCleaner cleaner, FontResolver fontResolver,int textColor, float textSize) {
99163
this.htmlCleaner = cleaner;
100164
this.fontResolver = fontResolver;
101165
this.handlers = new HashMap<String, TagNodeHandler>();
102-
166+
this.textColor=textColor;
167+
this.textSize=textSize;
103168
registerBuiltInHandlers();
104169
}
105170

@@ -119,6 +184,14 @@ public void setBackgroundColor(int backgroundColor) {
119184
this.backgroundColor = backgroundColor;
120185
}
121186

187+
public void setTextColor(int textColor) {
188+
this.textColor = textColor;
189+
}
190+
191+
public void setTextSize(float textSize) {
192+
this.textSize = textSize;
193+
}
194+
122195
/**
123196
* Switch to specify whether excess whitespace should be stripped from the
124197
* input.
@@ -205,6 +278,12 @@ public void unregisterHandler(String tagName) {
205278
* @return a Spanned version of the text.
206279
*/
207280
public Spannable fromHtml(String html) {
281+
if(html!=null){
282+
if(!TextUtils.isEmpty(html)){
283+
html=replaceHtmlTags(html);
284+
}
285+
}
286+
Log.i("HTML",html);
208287
return fromTagNode(this.htmlCleaner.clean(html), null);
209288
}
210289

@@ -371,6 +450,7 @@ private void registerBuiltInHandlers() {
371450
new Style().setFontWeight(Style.FontWeight.BOLD));
372451

373452
registerHandler("b", boldHandler);
453+
registerHandler("bold", boldHandler);
374454
registerHandler("strong", boldHandler);
375455
//Underline added
376456
registerHandler("u",new UnderlineHandler());
@@ -396,16 +476,13 @@ private void registerBuiltInHandlers() {
396476
TagNodeHandler brHandler = new NewLineHandler(1, inlineAlignment);
397477

398478
registerHandler("br", brHandler);
479+
registerHandler("br/", brHandler);
399480

400481
Style.BorderStyle borderStyle = Style.BorderStyle.valueOf("solid".toUpperCase());
401482

402483
//HR handler
403484
Style hrStyle = new Style()
404-
.setDisplayStyle(Style.DisplayStyle.BLOCK)
405-
.setMarginBottom(
406-
new StyleValue(1.0f, StyleValue.Unit.EM))
407-
.setBorderStyle(borderStyle).setBorderColor(Color.parseColor("#000000")).setBackgroundColor(backgroundColor);
408-
485+
.setDisplayStyle(Style.DisplayStyle.BLOCK);
409486

410487
TagNodeHandler hrHandler = new HorizontalLineHandler(wrap(new StyledTextHandler(hrStyle)));
411488

@@ -433,15 +510,16 @@ private void registerBuiltInHandlers() {
433510
registerHandler("span",spanHandler);
434511

435512
TableHandler tableHandler=new TableHandler();
436-
tableHandler.setTextSize(26f);
513+
tableHandler.setTextSize(textSize * 0.83f);
514+
tableHandler.setTextColor(textColor);
437515
registerHandler("table",tableHandler);
438516

439-
registerHandler("h1", wrap(new HeaderHandler(1.5f, 0.5f)));
440-
registerHandler("h2", wrap(new HeaderHandler(1.4f, 0.6f)));
441-
registerHandler("h3", wrap(new HeaderHandler(1.3f, 0.7f)));
442-
registerHandler("h4", wrap(new HeaderHandler(1.2f, 0.8f)));
443-
registerHandler("h5", wrap(new HeaderHandler(1.1f, 0.9f)));
444-
registerHandler("h6", wrap(new HeaderHandler(1f, 1f)));
517+
registerHandler("h1", wrap(new HeaderHandler(2f, 0.5f)));
518+
registerHandler("h2", wrap(new HeaderHandler(1.5f, 0.6f)));
519+
registerHandler("h3", wrap(new HeaderHandler(1.17f, 0.7f)));
520+
registerHandler("h4", wrap(new HeaderHandler(1.12f, 0.8f)));
521+
registerHandler("h5", wrap(new HeaderHandler(0.83f, 0.9f)));
522+
registerHandler("h6", wrap(new HeaderHandler(0.75f, 1f)));
445523

446524
TagNodeHandler preHandler = new PreHandler();
447525
registerHandler("pre", preHandler);
@@ -476,6 +554,19 @@ private void registerBuiltInHandlers() {
476554

477555
}
478556

557+
public static String replaceHtmlTags(String inputString) {
558+
if (inputString == null) {
559+
return null;
560+
}
561+
562+
// before apply typefaces, replace all tags
563+
for (Map.Entry<String, String> entry : htmlTagsDictionary.entrySet()) {
564+
inputString = inputString.replace(entry.getKey(), entry.getValue());
565+
inputString = inputString.replace(entry.getKey().toUpperCase(), entry.getValue());
566+
}
567+
return inputString;
568+
}
569+
479570
public static interface CancellationCallback {
480571
boolean isCancelled();
481572
}

html-textview-master/HtmlSpanner/src/main/java/net/nightwhistler/htmlspanner/css/CSSCompiler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.content.res.Resources;
44
import android.graphics.Color;
5+
import android.util.DisplayMetrics;
56
import android.util.Log;
67
import com.osbcp.cssparser.PropertyValue;
78
import com.osbcp.cssparser.Rule;
@@ -26,7 +27,7 @@
2627
*/
2728
public class CSSCompiler {
2829

29-
private static final int SCREEN_DENSITY=160;
30+
private static final float SCREEN_DENSITY=(Resources.getSystem().getDisplayMetrics().densityDpi)/ DisplayMetrics.DENSITY_DEFAULT;
3031

3132
public static interface StyleUpdater {
3233
Style updateStyle( Style style, HtmlSpanner spanner );
@@ -313,14 +314,13 @@ public Style updateStyle(Style style, HtmlSpanner spanner) {
313314

314315
if(styleValue.getUnit().equals(StyleValue.Unit.PX)){
315316
Log.i("value","int:"+styleValue.getIntValue());
316-
int dp=(int) ((styleValue.getIntValue()) / SCREEN_DENSITY);
317+
int dp=(int) ((styleValue.getIntValue()) * SCREEN_DENSITY);
317318
styleValue.setIntValue(dp);
318319
}
319320
// else{
320321
// Log.i("value"," float:"+styleValue.getFloatValue());
321322
// float f=styleValue.getFloatValue();
322-
// int dp=(int) (f / SCREEN_DENSITY);
323-
// styleValue.setFloatValue(Float.parseFloat(""+dp));
323+
// styleValue.setFloatValue(f/SCREEN_DENSITY);
324324
// }
325325
return new StyleUpdater() {
326326
@Override

html-textview-master/HtmlSpanner/src/main/java/net/nightwhistler/htmlspanner/handlers/ImageHandler.java

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,16 @@
2727
import android.graphics.BitmapFactory;
2828
import android.graphics.drawable.BitmapDrawable;
2929
import android.graphics.drawable.Drawable;
30+
import android.os.Handler;
3031
import android.text.Spannable;
3132
import android.text.SpannableStringBuilder;
3233
import android.text.style.ImageSpan;
34+
import android.util.Log;
35+
36+
import rx.Observable;
37+
import rx.Subscriber;
38+
import rx.android.schedulers.AndroidSchedulers;
39+
import rx.schedulers.Schedulers;
3340

3441
/**
3542
* Handles image tags.
@@ -42,23 +49,49 @@
4249
*/
4350
public class ImageHandler extends TagNodeHandler {
4451

45-
@Override
46-
public void handleTagNode(TagNode node, SpannableStringBuilder builder,
47-
int start, int end, SpanStack stack) {
52+
private static final String ERRORTAG = "Image error";
53+
private static final long MAXIMUM_TIME=450;
54+
55+
@Override
56+
public void handleTagNode(TagNode node, final SpannableStringBuilder builder,
57+
final int start, final int end, final SpanStack stack) {
4858
String src = node.getAttributeByName("src");
4959

5060
builder.append("\uFFFC");
5161

52-
Bitmap bitmap = loadBitmap(src);
62+
synchronized (this) {
63+
// we load the image on a different thread
64+
Observable.just(src).observeOn(Schedulers.io()).subscribe(new Subscriber<String>() {
65+
@Override
66+
public void onCompleted() {
67+
// do nothing
68+
}
5369

54-
if (bitmap != null) {
55-
Drawable drawable = new BitmapDrawable(bitmap);
56-
drawable.setBounds(0, 0, bitmap.getWidth() - 1,
57-
bitmap.getHeight() - 1);
70+
@Override
71+
public void onError(Throwable e) {
72+
Log.e(ERRORTAG, "" + e);
73+
}
5874

59-
stack.pushSpan( new ImageSpan(drawable), start, builder.length() );
60-
}
61-
}
75+
@Override
76+
public void onNext(String s) {
77+
Bitmap bitmap = loadBitmap(s);
78+
if (bitmap != null) {
79+
Drawable drawable = new BitmapDrawable(bitmap);
80+
drawable.setBounds(0, 0, bitmap.getWidth() - 1,
81+
bitmap.getHeight() - 1);
82+
stack.pushSpan(new ImageSpan(drawable), start, builder.length());
83+
}
84+
}
85+
});
86+
// we wait until the image is loaded,
87+
// if the image is not loaded until the maximum time we didn't show it
88+
try {
89+
this.wait(MAXIMUM_TIME);
90+
} catch (InterruptedException e) {
91+
e.printStackTrace();
92+
}
93+
}
94+
}
6295

6396
/**
6497
* Loads a Bitmap from the given url.

html-textview-master/HtmlSpanner/src/main/java/net/nightwhistler/htmlspanner/handlers/StyledTextHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public StyledTextHandler(Style style) {
2828
}
2929

3030
public Style getStyle() {
31+
style.setFontFamily(getSpanner().getFont("sans-serif"));
3132
return style;
3233
}
3334

html-textview-master/HtmlSpanner/src/main/java/net/nightwhistler/htmlspanner/handlers/TableHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@
5151
*/
5252
public class TableHandler extends TagNodeHandler {
5353

54-
private int tableWidth = 400;
54+
private int tableWidth = 500;
5555
private Typeface typeFace = Typeface.DEFAULT;
56-
private float textSize = 16f;
56+
private float textSize = 26f;
5757
private int textColor = Color.BLACK;
5858

5959
private static final int PADDING = 5;
@@ -75,6 +75,7 @@ public void setTableWidth(int tableWidth) {
7575
* @param textColor
7676
*/
7777
public void setTextColor(int textColor) {
78+
Log.i("color",""+textColor);
7879
this.textColor = textColor;
7980
}
8081

0 commit comments

Comments
 (0)