mirror of https://github.com/M66B/FairEmail.git
to HTML with font family
This commit is contained in:
parent
4a01c00f4e
commit
e4958c0874
|
@ -122,7 +122,7 @@ public class ActivityCompose extends ActivityBase implements FragmentManager.OnB
|
|||
CharSequence body = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
|
||||
if (body != null)
|
||||
if (body instanceof Spanned)
|
||||
args.putString("body", HtmlHelper.toHtml((Spanned) body));
|
||||
args.putString("body", HtmlHelper.toHtml((Spanned) body, this));
|
||||
else {
|
||||
String text = body.toString();
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
|
|
|
@ -194,7 +194,7 @@ public class ActivitySignature extends ActivityBase {
|
|||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(ActivitySignature.this, -1, source, true, 0, etText);
|
||||
}
|
||||
}, null));
|
||||
}, null, this));
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,9 @@ public class ActivitySignature extends ActivityBase {
|
|||
|
||||
private void save() {
|
||||
etText.clearComposingText();
|
||||
String html = (raw ? etText.getText().toString() : HtmlHelper.toHtml(etText.getText()));
|
||||
String html = (raw
|
||||
? etText.getText().toString()
|
||||
: HtmlHelper.toHtml(etText.getText(), this));
|
||||
Intent result = new Intent();
|
||||
result.putExtra("html", html);
|
||||
setResult(RESULT_OK, result);
|
||||
|
@ -218,7 +220,9 @@ public class ActivitySignature extends ActivityBase {
|
|||
this.raw = raw;
|
||||
|
||||
if (!raw || dirty) {
|
||||
String html = (raw ? HtmlHelper.toHtml(etText.getText()) : etText.getText().toString());
|
||||
String html = (raw
|
||||
? HtmlHelper.toHtml(etText.getText(), this)
|
||||
: etText.getText().toString());
|
||||
getIntent().putExtra("html", html);
|
||||
}
|
||||
|
||||
|
|
|
@ -3374,7 +3374,7 @@ class Core {
|
|||
|
||||
// Device
|
||||
builder.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(HtmlHelper.fromHtml(sb.toString()))
|
||||
.bigText(HtmlHelper.fromHtml(sb.toString(), context))
|
||||
.setSummaryText(title));
|
||||
}
|
||||
|
||||
|
@ -3663,7 +3663,7 @@ class Core {
|
|||
|
||||
if (sbm.length() > 0) {
|
||||
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle()
|
||||
.bigText(HtmlHelper.fromHtml(sbm.toString()));
|
||||
.bigText(HtmlHelper.fromHtml(sbm.toString(), context));
|
||||
if (!TextUtils.isEmpty(message.subject))
|
||||
bigText.setSummaryText(message.subject);
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ public class EditTextCompose extends FixedEditText {
|
|||
}
|
||||
|
||||
Document document = HtmlHelper.sanitizeCompose(context, html, false);
|
||||
Spanned paste = HtmlHelper.fromHtml(document.html(), new Html.ImageGetter() {
|
||||
Spanned paste = HtmlHelper.fromDocument(getContext(), document, new Html.ImageGetter() {
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(getContext(),
|
||||
|
|
|
@ -1373,7 +1373,7 @@ public class FragmentAccount extends FragmentBase {
|
|||
btnSupport.setVisibility(View.VISIBLE);
|
||||
|
||||
if (provider != null && provider.documentation != null) {
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString()));
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString(), getContext()));
|
||||
tvInstructions.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ public class FragmentAnswer extends FragmentBase {
|
|||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(getContext(), -1, source, true, 0, etText);
|
||||
}
|
||||
}, null));
|
||||
}, null, getContext()));
|
||||
}
|
||||
|
||||
bottom_navigation.findViewById(R.id.action_delete).setVisibility(answer == null ? View.GONE : View.VISIBLE);
|
||||
|
@ -236,7 +236,7 @@ public class FragmentAnswer extends FragmentBase {
|
|||
args.putString("name", etName.getText().toString().trim());
|
||||
args.putBoolean("favorite", cbFavorite.isChecked());
|
||||
args.putBoolean("hide", cbHide.isChecked());
|
||||
args.putString("text", HtmlHelper.toHtml(etText.getText()));
|
||||
args.putString("text", HtmlHelper.toHtml(etText.getText(), getContext()));
|
||||
|
||||
new SimpleTask<Void>() {
|
||||
@Override
|
||||
|
@ -425,7 +425,7 @@ public class FragmentAnswer extends FragmentBase {
|
|||
getString(R.string.title_answer_template_name) +
|
||||
"<br>" +
|
||||
getString(R.string.title_answer_template_email) +
|
||||
"</p>");
|
||||
"</p>", getContext());
|
||||
|
||||
View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ask_again, null);
|
||||
TextView tvMessage = dview.findViewById(R.id.tvMessage);
|
||||
|
|
|
@ -969,7 +969,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
Bundle args = new Bundle();
|
||||
args.putLong("id", working);
|
||||
args.putBoolean("plain", plain);
|
||||
args.putString("body", HtmlHelper.toHtml(etBody.getText()));
|
||||
args.putString("body", HtmlHelper.toHtml(etBody.getText(), getContext()));
|
||||
|
||||
new SimpleTask<String>() {
|
||||
@Override
|
||||
|
@ -1029,7 +1029,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
|
||||
private void deleteRef() {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("html", HtmlHelper.toHtml(etBody.getText()));
|
||||
extras.putString("html", HtmlHelper.toHtml(etBody.getText(), getContext()));
|
||||
extras.putBoolean("show", true);
|
||||
onAction(R.id.action_save, extras, "refdelete");
|
||||
}
|
||||
|
@ -1965,12 +1965,13 @@ public class FragmentCompose extends FragmentBase {
|
|||
|
||||
args.putInt("start", start);
|
||||
|
||||
return HtmlHelper.fromHtml(HtmlHelper.toHtml(s), new Html.ImageGetter() {
|
||||
// TODO: double conversion
|
||||
return HtmlHelper.fromHtml(HtmlHelper.toHtml(s, getContext()), new Html.ImageGetter() {
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(context, id, source, true, zoom, etBody);
|
||||
}
|
||||
}, null);
|
||||
}, null, getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2751,14 +2752,14 @@ public class FragmentCompose extends FragmentBase {
|
|||
} catch (AddressException ignored) {
|
||||
}
|
||||
|
||||
String text = EntityAnswer.replacePlaceholders(answer, to);
|
||||
String html = EntityAnswer.replacePlaceholders(answer, to);
|
||||
|
||||
Spanned spanned = HtmlHelper.fromHtml(text, new Html.ImageGetter() {
|
||||
Spanned spanned = HtmlHelper.fromHtml(html, new Html.ImageGetter() {
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(getContext(), working, source, true, zoom, etBody);
|
||||
}
|
||||
}, null);
|
||||
}, null, getContext());
|
||||
|
||||
etBody.getText().insert(etBody.getSelectionStart(), spanned);
|
||||
}
|
||||
|
@ -2802,7 +2803,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
if (!etSubject.getText().toString().equals(subject))
|
||||
return false;
|
||||
|
||||
if (!TextUtils.isEmpty(JsoupEx.parse(HtmlHelper.toHtml(etBody.getText())).text().trim()))
|
||||
if (!TextUtils.isEmpty(JsoupEx.parse(HtmlHelper.toHtml(etBody.getText(), getContext())).text().trim()))
|
||||
return false;
|
||||
|
||||
if (rvAttachment.getAdapter().getItemCount() > 0)
|
||||
|
@ -2831,7 +2832,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
args.putString("cc", etCc.getText().toString().trim());
|
||||
args.putString("bcc", etBcc.getText().toString().trim());
|
||||
args.putString("subject", etSubject.getText().toString().trim());
|
||||
args.putString("body", HtmlHelper.toHtml(etBody.getText()));
|
||||
args.putString("body", HtmlHelper.toHtml(etBody.getText(), getContext()));
|
||||
args.putBoolean("signature", cbSignature.isChecked());
|
||||
args.putBoolean("empty", isEmpty());
|
||||
args.putBoolean("interactive", getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED));
|
||||
|
@ -4629,7 +4630,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
Elements ref = doc.select("div[fairemail=reference]");
|
||||
ref.remove();
|
||||
|
||||
Spanned spannedBody = HtmlHelper.fromHtml(doc.html(), new Html.ImageGetter() {
|
||||
Spanned spannedBody = HtmlHelper.fromDocument(context, doc, new Html.ImageGetter() {
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(context, id, source, true, zoom, etBody);
|
||||
|
@ -4759,7 +4760,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
public Drawable getDrawable(String source) {
|
||||
return ImageHelper.decodeImage(getContext(), working, source, true, 0, tvSignature);
|
||||
}
|
||||
}, null);
|
||||
}, null, getContext());
|
||||
tvSignature.setText(signature);
|
||||
grpSignature.setVisibility(signature == null ? View.GONE : View.VISIBLE);
|
||||
|
||||
|
|
|
@ -995,7 +995,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
btnSupport.setVisibility(View.VISIBLE);
|
||||
|
||||
if (provider != null && provider.documentation != null) {
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString()));
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString(), getContext()));
|
||||
tvInstructions.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
|
|
@ -491,7 +491,7 @@ public class FragmentQuickSetup extends FragmentBase {
|
|||
btnSupport.setVisibility(View.VISIBLE);
|
||||
|
||||
if (args.containsKey("documentation")) {
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(args.getString("documentation")));
|
||||
tvInstructions.setText(HtmlHelper.fromHtml(args.getString("documentation"), getContext()));
|
||||
tvInstructions.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,479 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextDirectionHeuristics;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.AlignmentSpan;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.ParagraphStyle;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.SubscriptSpan;
|
||||
import android.text.style.SuperscriptSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import static android.text.Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE;
|
||||
|
||||
public class HtmlEx {
|
||||
private Context context;
|
||||
|
||||
private static final int TO_HTML_PARAGRAPH_FLAG = 0x00000001;
|
||||
|
||||
public HtmlEx(Context context){
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #toHtml(Spanned, int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public /* static */ String toHtml(Spanned text) {
|
||||
return toHtml(text, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML representation of the provided Spanned text. A best effort is
|
||||
* made to add HTML tags corresponding to spans. Also note that HTML metacharacters
|
||||
* (such as "<" and "&") within the input text are escaped.
|
||||
*
|
||||
* @param text input text to convert
|
||||
* @param option one of {@link #TO_HTML_PARAGRAPH_LINES_CONSECUTIVE} or
|
||||
* {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}
|
||||
* @return string containing input converted to HTML
|
||||
*/
|
||||
public /* static */ String toHtml(Spanned text, int option) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
withinHtml(out, text, option);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an HTML escaped representation of the given plain text.
|
||||
*/
|
||||
public /* static */ String escapeHtml(CharSequence text) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
withinStyle(out, text, 0, text.length());
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private /* static */ void withinHtml(StringBuilder out, Spanned text, int option) {
|
||||
if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) {
|
||||
encodeTextAlignmentByDiv(out, text, option);
|
||||
return;
|
||||
}
|
||||
|
||||
withinDiv(out, text, 0, text.length(), option);
|
||||
}
|
||||
|
||||
private /* static */ void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) {
|
||||
int len = text.length();
|
||||
|
||||
int next;
|
||||
for (int i = 0; i < len; i = next) {
|
||||
next = text.nextSpanTransition(i, len, ParagraphStyle.class);
|
||||
ParagraphStyle[] style = text.getSpans(i, next, ParagraphStyle.class);
|
||||
String elements = " ";
|
||||
boolean needDiv = false;
|
||||
|
||||
for(int j = 0; j < style.length; j++) {
|
||||
if (style[j] instanceof AlignmentSpan) {
|
||||
Layout.Alignment align =
|
||||
((AlignmentSpan) style[j]).getAlignment();
|
||||
needDiv = true;
|
||||
if (align == Layout.Alignment.ALIGN_CENTER) {
|
||||
elements = "align=\"center\" " + elements;
|
||||
} else if (align == Layout.Alignment.ALIGN_OPPOSITE) {
|
||||
elements = "align=\"right\" " + elements;
|
||||
} else {
|
||||
elements = "align=\"left\" " + elements;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needDiv) {
|
||||
out.append("<div ").append(elements).append(">");
|
||||
}
|
||||
|
||||
withinDiv(out, text, i, next, option);
|
||||
|
||||
if (needDiv) {
|
||||
out.append("</div>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private /* static */ void withinDiv(StringBuilder out, Spanned text, int start, int end,
|
||||
int option) {
|
||||
int next;
|
||||
for (int i = start; i < end; i = next) {
|
||||
next = text.nextSpanTransition(i, end, QuoteSpan.class);
|
||||
QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class);
|
||||
|
||||
for (QuoteSpan quote : quotes) {
|
||||
out.append("<blockquote>");
|
||||
}
|
||||
|
||||
withinBlockquote(out, text, i, next, option);
|
||||
|
||||
for (QuoteSpan quote : quotes) {
|
||||
out.append("</blockquote>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private /* static */ String getTextDirection(Spanned text, int start, int end) {
|
||||
if (TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(text, start, end - start)) {
|
||||
return " dir=\"rtl\"";
|
||||
} else {
|
||||
return " dir=\"ltr\"";
|
||||
}
|
||||
}
|
||||
|
||||
private /* static */ String getTextStyles(Spanned text, int start, int end,
|
||||
boolean forceNoVerticalMargin, boolean includeTextAlign) {
|
||||
String margin = null;
|
||||
String textAlign = null;
|
||||
|
||||
if (forceNoVerticalMargin) {
|
||||
margin = "margin-top:0; margin-bottom:0;";
|
||||
}
|
||||
if (includeTextAlign) {
|
||||
final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
|
||||
|
||||
// Only use the last AlignmentSpan with flag SPAN_PARAGRAPH
|
||||
for (int i = alignmentSpans.length - 1; i >= 0; i--) {
|
||||
AlignmentSpan s = alignmentSpans[i];
|
||||
if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) {
|
||||
final Layout.Alignment alignment = s.getAlignment();
|
||||
if (alignment == Layout.Alignment.ALIGN_NORMAL) {
|
||||
textAlign = "text-align:start;";
|
||||
} else if (alignment == Layout.Alignment.ALIGN_CENTER) {
|
||||
textAlign = "text-align:center;";
|
||||
} else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
|
||||
textAlign = "text-align:end;";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (margin == null && textAlign == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final StringBuilder style = new StringBuilder(" style=\"");
|
||||
if (margin != null && textAlign != null) {
|
||||
style.append(margin).append(" ").append(textAlign);
|
||||
} else if (margin != null) {
|
||||
style.append(margin);
|
||||
} else if (textAlign != null) {
|
||||
style.append(textAlign);
|
||||
}
|
||||
|
||||
return style.append("\"").toString();
|
||||
}
|
||||
|
||||
private /* static */ void withinBlockquote(StringBuilder out, Spanned text, int start, int end,
|
||||
int option) {
|
||||
if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) {
|
||||
withinBlockquoteConsecutive(out, text, start, end);
|
||||
} else {
|
||||
withinBlockquoteIndividual(out, text, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
private /* static */ void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start,
|
||||
int end) {
|
||||
boolean isInList = false;
|
||||
int next;
|
||||
for (int i = start; i <= end; i = next) {
|
||||
next = TextUtils.indexOf(text, '\n', i, end);
|
||||
if (next < 0) {
|
||||
next = end;
|
||||
}
|
||||
|
||||
if (next == i) {
|
||||
if (isInList) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
}
|
||||
out.append("<br>\n");
|
||||
} else {
|
||||
boolean isListItem = false;
|
||||
ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
|
||||
for (ParagraphStyle paragraphStyle : paragraphStyles) {
|
||||
final int spanFlags = text.getSpanFlags(paragraphStyle);
|
||||
if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
|
||||
&& paragraphStyle instanceof BulletSpan) {
|
||||
isListItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isListItem && !isInList) {
|
||||
// Current paragraph is the first item in a list
|
||||
isInList = true;
|
||||
out.append("<ul")
|
||||
.append(getTextStyles(text, i, next, true, false))
|
||||
.append(">\n");
|
||||
}
|
||||
|
||||
if (isInList && !isListItem) {
|
||||
// Current paragraph is no longer a list item; close the previously opened list
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
}
|
||||
|
||||
String tagType = isListItem ? "li" : "p";
|
||||
out.append("<").append(tagType)
|
||||
.append(getTextDirection(text, i, next))
|
||||
.append(getTextStyles(text, i, next, !isListItem, true))
|
||||
.append(">");
|
||||
|
||||
withinParagraph(out, text, i, next);
|
||||
|
||||
out.append("</");
|
||||
out.append(tagType);
|
||||
out.append(">\n");
|
||||
|
||||
if (next == end && isInList) {
|
||||
isInList = false;
|
||||
out.append("</ul>\n");
|
||||
}
|
||||
}
|
||||
|
||||
next++;
|
||||
}
|
||||
}
|
||||
|
||||
private /* static */ void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start,
|
||||
int end) {
|
||||
out.append("<p").append(getTextDirection(text, start, end)).append(">");
|
||||
|
||||
int next;
|
||||
for (int i = start; i < end; i = next) {
|
||||
next = TextUtils.indexOf(text, '\n', i, end);
|
||||
if (next < 0) {
|
||||
next = end;
|
||||
}
|
||||
|
||||
int nl = 0;
|
||||
|
||||
while (next < end && text.charAt(next) == '\n') {
|
||||
nl++;
|
||||
next++;
|
||||
}
|
||||
|
||||
withinParagraph(out, text, i, next - nl);
|
||||
|
||||
if (nl == 1) {
|
||||
out.append("<br>\n");
|
||||
} else {
|
||||
for (int j = 2; j < nl; j++) {
|
||||
out.append("<br>");
|
||||
}
|
||||
if (next != end) {
|
||||
/* Paragraph should be closed and reopened */
|
||||
out.append("</p>\n");
|
||||
out.append("<p").append(getTextDirection(text, start, end)).append(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.append("</p>\n");
|
||||
}
|
||||
|
||||
private /* static */ void withinParagraph(StringBuilder out, Spanned text, int start, int end) {
|
||||
int next;
|
||||
for (int i = start; i < end; i = next) {
|
||||
next = text.nextSpanTransition(i, end, CharacterStyle.class);
|
||||
CharacterStyle[] style = text.getSpans(i, next, CharacterStyle.class);
|
||||
|
||||
for (int j = 0; j < style.length; j++) {
|
||||
if (style[j] instanceof StyleSpan) {
|
||||
int s = ((StyleSpan) style[j]).getStyle();
|
||||
|
||||
if ((s & Typeface.BOLD) != 0) {
|
||||
out.append("<b>");
|
||||
}
|
||||
if ((s & Typeface.ITALIC) != 0) {
|
||||
out.append("<i>");
|
||||
}
|
||||
}
|
||||
if (style[j] instanceof TypefaceSpan) {
|
||||
String s = ((TypefaceSpan) style[j]).getFamily();
|
||||
|
||||
//if ("monospace".equals(s)) {
|
||||
// out.append("<tt>");
|
||||
//}
|
||||
|
||||
out.append("<span style=\"font-family:" + s + ";\">");
|
||||
}
|
||||
if (style[j] instanceof SuperscriptSpan) {
|
||||
out.append("<sup>");
|
||||
}
|
||||
if (style[j] instanceof SubscriptSpan) {
|
||||
out.append("<sub>");
|
||||
}
|
||||
if (style[j] instanceof UnderlineSpan) {
|
||||
out.append("<u>");
|
||||
}
|
||||
if (style[j] instanceof StrikethroughSpan) {
|
||||
out.append("<span style=\"text-decoration:line-through;\">");
|
||||
}
|
||||
if (style[j] instanceof URLSpan) {
|
||||
out.append("<a href=\"");
|
||||
out.append(((URLSpan) style[j]).getURL());
|
||||
out.append("\">");
|
||||
}
|
||||
if (style[j] instanceof ImageSpan) {
|
||||
out.append("<img src=\"");
|
||||
out.append(((ImageSpan) style[j]).getSource());
|
||||
out.append("\">");
|
||||
|
||||
// Don't output the dummy character underlying the image.
|
||||
i = next;
|
||||
}
|
||||
if (style[j] instanceof AbsoluteSizeSpan) {
|
||||
AbsoluteSizeSpan s = ((AbsoluteSizeSpan) style[j]);
|
||||
float sizeDip = s.getSize();
|
||||
if (!s.getDip()) {
|
||||
//Application application = ActivityThread.currentApplication();
|
||||
sizeDip /= context.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
// px in CSS is the equivalance of dip in Android
|
||||
out.append(String.format("<span style=\"font-size:%.0fpx\";>", sizeDip));
|
||||
}
|
||||
if (style[j] instanceof RelativeSizeSpan) {
|
||||
float sizeEm = ((RelativeSizeSpan) style[j]).getSizeChange();
|
||||
out.append(String.format("<span style=\"font-size:%.2fem;\">", sizeEm));
|
||||
}
|
||||
if (style[j] instanceof ForegroundColorSpan) {
|
||||
int color = ((ForegroundColorSpan) style[j]).getForegroundColor();
|
||||
out.append(String.format("<span style=\"color:#%06X;\">", 0xFFFFFF & color));
|
||||
}
|
||||
if (style[j] instanceof BackgroundColorSpan) {
|
||||
int color = ((BackgroundColorSpan) style[j]).getBackgroundColor();
|
||||
out.append(String.format("<span style=\"background-color:#%06X;\">",
|
||||
0xFFFFFF & color));
|
||||
}
|
||||
}
|
||||
|
||||
withinStyle(out, text, i, next);
|
||||
|
||||
for (int j = style.length - 1; j >= 0; j--) {
|
||||
if (style[j] instanceof BackgroundColorSpan) {
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof ForegroundColorSpan) {
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof RelativeSizeSpan) {
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof AbsoluteSizeSpan) {
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof URLSpan) {
|
||||
out.append("</a>");
|
||||
}
|
||||
if (style[j] instanceof StrikethroughSpan) {
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof UnderlineSpan) {
|
||||
out.append("</u>");
|
||||
}
|
||||
if (style[j] instanceof SubscriptSpan) {
|
||||
out.append("</sub>");
|
||||
}
|
||||
if (style[j] instanceof SuperscriptSpan) {
|
||||
out.append("</sup>");
|
||||
}
|
||||
if (style[j] instanceof TypefaceSpan) {
|
||||
//String s = ((TypefaceSpan) style[j]).getFamily();
|
||||
|
||||
//if (s.equals("monospace")) {
|
||||
// out.append("</tt>");
|
||||
//}
|
||||
|
||||
out.append("</span>");
|
||||
}
|
||||
if (style[j] instanceof StyleSpan) {
|
||||
int s = ((StyleSpan) style[j]).getStyle();
|
||||
|
||||
if ((s & Typeface.BOLD) != 0) {
|
||||
out.append("</b>");
|
||||
}
|
||||
if ((s & Typeface.ITALIC) != 0) {
|
||||
out.append("</i>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@UnsupportedAppUsage
|
||||
private /* static */ void withinStyle(StringBuilder out, CharSequence text,
|
||||
int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = text.charAt(i);
|
||||
|
||||
if (c == '<') {
|
||||
out.append("<");
|
||||
} else if (c == '>') {
|
||||
out.append(">");
|
||||
} else if (c == '&') {
|
||||
out.append("&");
|
||||
} else if (c >= 0xD800 && c <= 0xDFFF) {
|
||||
if (c < 0xDC00 && i + 1 < end) {
|
||||
char d = text.charAt(i + 1);
|
||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
||||
i++;
|
||||
int codepoint = 0x010000 | (int) c - 0xD800 << 10 | (int) d - 0xDC00;
|
||||
out.append("&#").append(codepoint).append(";");
|
||||
}
|
||||
}
|
||||
} else if (c > 0x7E || c < ' ') {
|
||||
out.append("&#").append((int) c).append(";");
|
||||
} else if (c == ' ') {
|
||||
while (i + 1 < end && text.charAt(i + 1) == ' ') {
|
||||
out.append(" ");
|
||||
i++;
|
||||
}
|
||||
|
||||
out.append(' ');
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,7 +62,6 @@ import androidx.annotation.Nullable;
|
|||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import androidx.core.util.PatternsCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
|
@ -112,7 +111,6 @@ import java.util.Map;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static androidx.core.text.HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM;
|
||||
import static androidx.core.text.HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE;
|
||||
import static org.w3c.css.sac.Condition.SAC_CLASS_CONDITION;
|
||||
|
||||
|
@ -2221,11 +2219,14 @@ public class HtmlHelper {
|
|||
return ssb;
|
||||
}
|
||||
|
||||
static Spanned fromHtml(@NonNull String html) {
|
||||
return fromHtml(html, null, null);
|
||||
static Spanned fromHtml(@NonNull String html, Context context) {
|
||||
return fromHtml(html, null, null, context);
|
||||
}
|
||||
|
||||
static Spanned fromHtml(@NonNull String html, @Nullable Html.ImageGetter imageGetter, @Nullable Html.TagHandler tagHandler) {
|
||||
static Spanned fromHtml(@NonNull String html, @Nullable Html.ImageGetter imageGetter, @Nullable Html.TagHandler tagHandler, Context context) {
|
||||
Document document = JsoupEx.parse(html);
|
||||
return fromDocument(context, document, false, imageGetter, tagHandler);
|
||||
/*
|
||||
Spanned spanned = HtmlCompat.fromHtml(html, FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM, imageGetter, tagHandler);
|
||||
|
||||
int i = spanned.length();
|
||||
|
@ -2235,10 +2236,12 @@ public class HtmlHelper {
|
|||
spanned = (Spanned) spanned.subSequence(0, i);
|
||||
|
||||
return reverseSpans(spanned);
|
||||
*/
|
||||
}
|
||||
|
||||
static String toHtml(Spanned spanned) {
|
||||
String html = HtmlCompat.toHtml(spanned, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
|
||||
static String toHtml(Spanned spanned, Context context) {
|
||||
HtmlEx converter = new HtmlEx(context);
|
||||
String html = converter.toHtml(spanned, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
|
||||
|
||||
// @Google: why convert size to and from in a different way?
|
||||
Document doc = JsoupEx.parse(html);
|
||||
|
|
Loading…
Reference in New Issue