一、需求
规则:
通用:
- 所有替换针对行内样式,即:
style="font-weight: bold; font-style: italic;"
style="font-size: 24px; color: #333"
- 分两大类:
- 标签类
- 样式类
具体:
-
标签类
font-weight: bold;
-> 此标签内部内容用<b>...</b>
包裹,同时去掉font-weight: bold;
font-style: italic;
-> 此标签内部内容用<i>...</i>
包裹,同时去掉font-style: italic;
text-decoration: underline;
-> 此标签内部内容用<u>...</u>
包裹,同时去掉text-decoration: underline;
font-weight: normal;
-> 直接干掉该样式font-style: normal;
-> 直接干掉该样式text-decoration: none;
-> 直接干掉该样式
-
样式类
color: #333;
-> 此标签内部内容用<font color="#333">...</font>
包裹,同时去掉color: #333;
font-size: 24px;
-> 此标签内部内容用<font size="5">...</font>
包裹,同时去掉font-size: 24px;
- 以上两种都存在,在只需要 1个
<font>...</font>
即可
最后,去掉 含有
style=""
的标签 -
font-size 转化规则
32px
->6
24px
->5
18px
->4
16px
->3
13px
->2
12px
->1
before:
<p style="font-weight: bold; font-style: italic; text-decoration: underline;">
<a href="https://www.kaola.com"
target="_blank"
style="font-size: 24px; color: #333">
这是一段文本
</a>
</p>
<p style="font-weight: bold;">
<img src="https://i.loli.net/2017/12/28/5a44ef4cb0672.jpg">
</p>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
after:
<p>
<b>
<i>
<u>
<a href="https://www.kaola.com" target="_blank">
<font size="5" color="#333">
这是一段文本
</font>
</a>
</u>
</i>
</b>
</p>
<p>
<b>
<img src="https://i.loli.net/2017/12/28/5a44ef4cb0672.jpg">
</b>
</p>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
二、思路
字符串替换。
1.先找到带style属性的标签,然后按规则替换成标签;如果颜色用rgb表示的,要转成十六进制;
2.用Jsoup解析一遍,填补结束标签;
3.Jsoup解析,删除所有style属性
三、代码
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HtmlStyleParser {
private HtmlStyleParser(){
}
private volatile static HtmlStyleParser instance;
public static HtmlStyleParser getInstance(){
if (instance == null) {
synchronized (HtmlStyleParser.class) {
if (instance == null) {
instance = new HtmlStyleParser();
}
}
}
return instance;
}
public String parse(String html) {
//把style属性转成标签
String htmlStyle2Tag = style2Tag(html);
if (StringUtils.isBlank(htmlStyle2Tag)){
return null;
}
//用Jsoup解析,填补结束标签
Document document = Jsoup.parse(htmlStyle2Tag, "", Parser.xmlParser());
Elements elements = document.select("[style]");
if (CollectionUtils.isEmpty(elements)) {
return htmlStyle2Tag;
}
for (Element element : elements) {
element.removeAttr("style");
}
return document.toString();
}
/**
* 解析CSS style属性,转成标签
* @param html
* @return
*/
public String style2Tag(String html) {
//取出style属性
String regex = "<(\\s*?(\\w+)[\\s\\S]*?(style[\\s\\S]*?=\"([\\s\\S]*?)\"[\\s\\S]*?))>";
Pattern p = Pattern.compile(regex, Pattern.MULTILINE);
Matcher m = p.matcher(html);
String newHtml = html;
Set<String> findedTags = new HashSet<>(); //匹配到之后将匹配到的字符串放进来,防止重复匹配
while (m.find()) {
//<p style="font-weight: bold; font-style: italic; text-decoration: underline;">
String tagAndStyle = m.group();
if (StringUtils.isBlank(tagAndStyle) || findedTags.contains(tagAndStyle)) {
continue;
}
String styleVal = m.group(4).trim();
if (StringUtils.isBlank(styleVal)) {
continue;
}
//font-weight: bold; font-style: normal;
String[] styleArray = styleVal.split(";");
if (ArrayUtils.isEmpty(styleArray)) {
continue;
}
Set<String> tagSet = new TreeSet<>(); //存<b>、<i>、<u>等标签名
Set<String> fontSet = new TreeSet<>(); //存font-size、color
//style转成tag
this.convertTagAndFont(styleArray, tagSet, fontSet);
String tagStr = null;
if (CollectionUtils.isNotEmpty(tagSet)) {
tagStr = StringUtils.join(tagSet.toArray());
}
String fontStr = null;
if (CollectionUtils.isNotEmpty(fontSet)) {
fontStr = "<font " + StringUtils.join(fontSet.toArray(), " ") + ">";
}
StringBuilder newTagProperties = new StringBuilder(tagAndStyle);
if (StringUtils.isNotBlank(tagStr)) {
newTagProperties.append(tagStr);
}
if (StringUtils.isNotBlank(fontStr)) {
newTagProperties.append(fontStr);
}
newHtml = newHtml.replace(tagAndStyle, newTagProperties.toString());
findedTags.add(tagAndStyle);
}
return newHtml;
}
/**
* 把css的style属性解析成tag
* @param styleArray
* @param tagSet
* @param fontSet
*/
private void convertTagAndFont(String[] styleArray, Set<String> tagSet, Set<String> fontSet) {
if (ArrayUtils.isEmpty(styleArray) || tagSet == null || fontSet == null) {
return;
}
for (String styleStr : styleArray) {
if (StringUtils.isBlank(styleStr) || !styleStr.contains(":")) {
continue;
}
String[] styleKeyVal = styleStr.split(":");
if (ArrayUtils.isEmpty(styleKeyVal) || styleKeyVal.length < 2) {
continue;
}
if (StringUtils.isBlank(styleKeyVal[0]) || StringUtils.isBlank(styleKeyVal[1])) {
continue;
}
String styleKey = styleKeyVal[0].trim();
String styleVal = styleKeyVal[1].trim();
if ("font-weight".equalsIgnoreCase(styleKey) && "bold".equalsIgnoreCase(styleVal)) {
tagSet.add("<b>");
} else if ("font-style".equalsIgnoreCase(styleKey) && "italic".equalsIgnoreCase(styleVal)) {
tagSet.add("<i>");
} else if ("text-decoration".equalsIgnoreCase(styleKey) && "underline".equalsIgnoreCase(styleVal)) {
tagSet.add("<u>");
} else if ("font-size".equalsIgnoreCase(styleKey) && FONT_SIZE_MAP.containsKey(styleVal)) {
fontSet.add("size=" + FONT_SIZE_MAP.get(styleVal));
} else if ("color".equalsIgnoreCase(styleKey)) {
fontSet.add(styleKey + "=" + rgb2HexIfExists(styleVal));
}
}
}
/**
* rgb格式的颜色转成十六进制。如果不是rgb,则返回原样。
* @param rgb
* @return
*/
private String rgb2HexIfExists(String rgb) {
if (StringUtils.isBlank(rgb)) {
return null;
}
String rgbRegex = "rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)";
Pattern p = Pattern.compile(rgbRegex);
Matcher m1 = p.matcher(rgb);
try {
while (m1.find()) {
String r = m1.group(1);
String g = m1.group(2);
String b = m1.group(3);
rgb = String.format("#%02x%02x%02x", Integer.valueOf(r), Integer.valueOf(g), Integer.valueOf(b));
}
} catch (Exception e) {
e.printStackTrace();
}
return rgb;
}
//字体大小映射
private static final Map<String, Integer> FONT_SIZE_MAP = new HashMap<String, Integer>(6){{
put("32px", 6);
put("24px", 5);
put("18px", 4);
put("16px", 3);
put("13px", 2);
put("12px", 1);
}};
}