android端做过epub阅读器的小伙伴应该对这个都不陌生了,可谓是又爱又恨.既爱其强大,又恨自己看不懂(可能只是我一人).
我先讲下背景,公司是做数字阅读的,而我们选择了FBReader作为epub的文件解析器,在我接手之前已经用了好几年了(公司之前的大佬集成进去的),而我(不会c++的android菜鸡)来的2年期间也没有深入了解过.随之而来的就是慢慢的折磨,遇到了以下几个问题.
一是新制作的epub结构不兼容FBReader结构导致无法阅读,那时排查了3天才发现是解析路径出了问题,最后勉强修复了这个问题.在github上有提过fbreader的请求分支,大家可以看看这种low操作.
ZLFile.cpp 的 ZLFile::ZLFile()方法中
//兼容content.opf在根目录的epub制作规则
myPath = ZLStringUtil::replace(myPath, ":./", ":");
二是无法读取epub文件中的css样式导致排版太丑,同样是路径的问题.
XTHMLReader.cpp 的 void XHTMLTagLinkAction::doAtStart()方法中
cssFilePath = ZLStringUtil::replace(cssFilePath, ":", "/");
三是极少数设备so库100%崩溃的问题,都是先try(){}catch()看是否影响正常阅读,如果不影响则就用这种low办法处理.
一年前公司计划(其实这应该是常规功能)要多端同步!当时我把这个功能分给另一个同事了,后来也不知道进展,今年疫情由于某种原因离职了(当然不是公司的原因咯),看来这始终还得折磨我弱小的心理.
于是我开始恶补FBReader的相关文档,此处得感谢大佬的文章让我初步了解了解析流程.因为多端都是各自的方案实现的,所以我们在epub上的每个段落p标签标记属性id来同步.FbReader其实已经对已实现的标签中(XHTMLReader:fillTagTable())的id属性是有解析的,在XHTMLReader::startElementHandler()中可以找到根据,但存储的是myReferenceAlias + # + id
,与我们实际要的id多myReferenceAlias + #
,所以在取id对应的段位置时需要注意,具体取id对应的段位置在BookModel.getLabelInternal()
中,根据需求,需要调整一下实现
public Label getLabelInternal(String id) {
final int len = id.length();
final int size = myInternalHyperlinks.size();
for (int i = 0; i < size; ++i) {
final char[] block = myInternalHyperlinks.block(i);
for (int offset = 0; offset < block.length; ) {
final int labelLength = (int)block[offset++];
if (labelLength == 0) {
break;
}
final int idLength = (int)block[offset + labelLength];
String idCache = new String(block, offset, labelLength);
//TODO 获取属性id BK3468093014-37对应的段位置,但myInternalHyperlinks已此方式拼接9#BK3468093014-37,
// 所以判断lengh的条件去掉, equals()修改为contains()
// 详细拼接见XHTMLReader.cpp中addHyperlinkLabel(),同理也可增加其他属性
//if (labelLength != len || !id.equals(idCache)) {
if (!idCache.contains(id)) {
offset += labelLength + idLength + 3;
continue;
}
offset += labelLength + 1;
final String modelId = (idLength > 0) ? new String(block, offset, idLength) : null;
offset += idLength;
final int paragraphNumber = (int)block[offset] + (((int)block[offset + 1]) << 16);
return new Label(modelId, paragraphNumber);
}
}
return null;
}
以上就是根据已知的属性id取到对应的位置,然后跟目录一样的操作跳转就可以跳到指定的id位置了.
后面还需要解决的是如何根据当前阅读页的段落取到其属性id,作为同步的依据.如果细化的话,对每段中每句都加上属性id该怎么处理?
还在研究中,若有大佬,烦请赐教!!