pull/1434/head
gedoor 3 years ago
parent e2d187f38a
commit 493524b402
  1. 27
      app/src/main/java/io/legado/app/model/localBook/EpubFile.kt
  2. 424
      epublib/src/main/java/me/ag2s/epublib/domain/TableOfContents.java

@ -119,23 +119,18 @@ class EpubFile(var book: Book) {
private fun getContent(chapter: BookChapter): String? { private fun getContent(chapter: BookChapter): String? {
/*获取当前章节文本*/ /*获取当前章节文本*/
return getChildChapter(chapter, chapter.url)
}
private fun getChildChapter(chapter: BookChapter, href: String): String? {
epubBook?.let { epubBook?.let {
val body = Jsoup.parse(String(it.resources.getByHref(href).data, mCharset)).body() val body =
Jsoup.parse(String(it.resources.getByHref(chapter.url).data, mCharset)).body()
if (chapter.url == href) {
val startFragmentId = chapter.startFragmentId val startFragmentId = chapter.startFragmentId
val endFragmentId = chapter.endFragmentId val endFragmentId = chapter.endFragmentId
/*一些书籍依靠href索引的resource会包含多个章节,需要依靠fragmentId来截取到当前章节的内容*/ /*一些书籍依靠href索引的resource会包含多个章节,需要依靠fragmentId来截取到当前章节的内容*/
/*注:这里较大增加了内容加载的时间,所以首次获取内容后可存储到本地cache,减少重复加载*/ /*注:这里较大增加了内容加载的时间,所以首次获取内容后可存储到本地cache,减少重复加载*/
if (!startFragmentId.isNullOrBlank()) if (!startFragmentId.isNullOrBlank())
body.getElementById(startFragmentId)?.previousElementSiblings()?.remove() body.getElementById(startFragmentId)?.previousElementSiblings()?.remove()
if (!endFragmentId.isNullOrBlank() && endFragmentId != startFragmentId) if (!endFragmentId.isNullOrBlank() && endFragmentId != startFragmentId)
body.getElementById(endFragmentId)?.nextElementSiblings()?.remove() body.getElementById(endFragmentId)?.nextElementSiblings()?.remove()
}
/*选择去除正文中的H标签,部分书籍标题与阅读标题重复待优化*/ /*选择去除正文中的H标签,部分书籍标题与阅读标题重复待优化*/
var tag = Book.hTag var tag = Book.hTag

@ -10,251 +10,255 @@ import java.util.Set;
/** /**
* The table of contents of the book. * The table of contents of the book.
* The TableOfContents is a tree structure at the root it is a list of TOCReferences, each if which may have as children another list of TOCReferences. * The TableOfContents is a tree structure at the root it is a list of TOCReferences, each if which may have as children another list of TOCReferences.
* * <p>
* The table of contents is used by epub as a quick index to chapters and sections within chapters. * The table of contents is used by epub as a quick index to chapters and sections within chapters.
* It may contain duplicate entries, may decide to point not to certain chapters, etc. * It may contain duplicate entries, may decide to point not to certain chapters, etc.
* * <p>
* See the spine for the complete list of sections in the order in which they should be read. * See the spine for the complete list of sections in the order in which they should be read.
* *
* @see Spine
*
* @author paul * @author paul
* @see Spine
*/ */
public class TableOfContents implements Serializable { public class TableOfContents implements Serializable {
private static final long serialVersionUID = -3147391239966275152L; private static final long serialVersionUID = -3147391239966275152L;
public static final String DEFAULT_PATH_SEPARATOR = "/"; public static final String DEFAULT_PATH_SEPARATOR = "/";
private List<TOCReference> tocReferences; private List<TOCReference> tocReferences;
public TableOfContents() { public TableOfContents() {
this(new ArrayList<>()); this(new ArrayList<>());
} }
public TableOfContents(List<TOCReference> tocReferences) {
this.tocReferences = tocReferences;
}
public List<TOCReference> getTocReferences() {
return tocReferences;
}
public void setTocReferences(List<TOCReference> tocReferences) {
this.tocReferences = tocReferences;
}
/** public TableOfContents(List<TOCReference> tocReferences) {
* Calls addTOCReferenceAtLocation after splitting the path using the DEFAULT_PATH_SEPARATOR. this.tocReferences = tocReferences;
* @return the new TOCReference }
*/
@SuppressWarnings("unused")
public TOCReference addSection(Resource resource, String path) {
return addSection(resource, path, DEFAULT_PATH_SEPARATOR);
}
/** public List<TOCReference> getTocReferences() {
* Calls addTOCReferenceAtLocation after splitting the path using the given pathSeparator. return tocReferences;
* }
* @param resource resource
* @param path path
* @param pathSeparator pathSeparator
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String path,
String pathSeparator) {
String[] pathElements = path.split(pathSeparator);
return addSection(resource, pathElements);
}
/** public void setTocReferences(List<TOCReference> tocReferences) {
* Finds the first TOCReference in the given list that has the same title as the given Title. this.tocReferences = tocReferences;
*
* @param title title
* @param tocReferences tocReferences
* @return null if not found.
*/
private static TOCReference findTocReferenceByTitle(String title,
List<TOCReference> tocReferences) {
for (TOCReference tocReference : tocReferences) {
if (title.equals(tocReference.getTitle())) {
return tocReference;
}
} }
return null;
}
/** /**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements. * Calls addTOCReferenceAtLocation after splitting the path using the DEFAULT_PATH_SEPARATOR.
* *
* Example: * @return the new TOCReference
* Calling this method with a Resource and new String[] {"chapter1", "paragraph1"} will result in the following: */
* <ul> @SuppressWarnings("unused")
* <li>a TOCReference with the title "chapter1" at the root level.<br/> public TOCReference addSection(Resource resource, String path) {
* If this TOCReference did not yet exist it will have been created and does not point to any resource</li> return addSection(resource, path, DEFAULT_PATH_SEPARATOR);
* <li>A TOCReference that has the title "paragraph1". This TOCReference will be the child of TOCReference "chapter1" and
* will point to the given Resource</li>
* </ul>
*
* @param resource resource
* @param pathElements pathElements
* @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String[] pathElements) {
if (pathElements == null || pathElements.length == 0) {
return null;
} }
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences; /**
for (String currentTitle : pathElements) { * Calls addTOCReferenceAtLocation after splitting the path using the given pathSeparator.
result = findTocReferenceByTitle(currentTitle, currentTocReferences); *
if (result == null) { * @param resource resource
result = new TOCReference(currentTitle, null); * @param path path
currentTocReferences.add(result); * @param pathSeparator pathSeparator
} * @return the new TOCReference
currentTocReferences = result.getChildren(); */
public TOCReference addSection(Resource resource, String path,
String pathSeparator) {
String[] pathElements = path.split(pathSeparator);
return addSection(resource, pathElements);
} }
result.setResource(resource);
return result;
}
/** /**
* Adds the given Resources to the TableOfContents at the location specified by the pathElements. * Finds the first TOCReference in the given list that has the same title as the given Title.
* *
* Example: * @param title title
* Calling this method with a Resource and new int[] {0, 0} will result in the following: * @param tocReferences tocReferences
* <ul> * @return null if not found.
* <li>a TOCReference at the root level.<br/> */
* If this TOCReference did not yet exist it will have been created with a title of "" and does not point to any resource</li> private static TOCReference findTocReferenceByTitle(String title,
* <li>A TOCReference that points to the given resource and is a child of the previously created TOCReference.<br/> List<TOCReference> tocReferences) {
* If this TOCReference didn't exist yet it will be created and have a title of ""</li> for (TOCReference tocReference : tocReferences) {
* </ul> if (title.equals(tocReference.getTitle())) {
* return tocReference;
* @param resource resource }
* @param pathElements pathElements }
* @return the new TOCReference return null;
*/
@SuppressWarnings("unused")
public TOCReference addSection(Resource resource, int[] pathElements,
String sectionTitlePrefix, String sectionNumberSeparator) {
if (pathElements == null || pathElements.length == 0) {
return null;
} }
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences; /**
for (int i = 0; i < pathElements.length; i++) { * Adds the given Resources to the TableOfContents at the location specified by the pathElements.
int currentIndex = pathElements[i]; * <p>
if (currentIndex > 0 && currentIndex < (currentTocReferences.size() * Example:
- 1)) { * Calling this method with a Resource and new String[] {"chapter1", "paragraph1"} will result in the following:
result = currentTocReferences.get(currentIndex); * <ul>
} else { * <li>a TOCReference with the title "chapter1" at the root level.<br/>
result = null; * If this TOCReference did not yet exist it will have been created and does not point to any resource</li>
} * <li>A TOCReference that has the title "paragraph1". This TOCReference will be the child of TOCReference "chapter1" and
if (result == null) { * will point to the given Resource</li>
paddTOCReferences(currentTocReferences, pathElements, i, * </ul>
sectionTitlePrefix, sectionNumberSeparator); *
result = currentTocReferences.get(currentIndex); * @param resource resource
} * @param pathElements pathElements
currentTocReferences = result.getChildren(); * @return the new TOCReference
*/
public TOCReference addSection(Resource resource, String[] pathElements) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences;
for (String currentTitle : pathElements) {
result = findTocReferenceByTitle(currentTitle, currentTocReferences);
if (result == null) {
result = new TOCReference(currentTitle, null);
currentTocReferences.add(result);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
} }
result.setResource(resource);
return result;
}
private void paddTOCReferences(List<TOCReference> currentTocReferences, /**
int[] pathElements, int pathPos, String sectionPrefix, * Adds the given Resources to the TableOfContents at the location specified by the pathElements.
String sectionNumberSeparator) { * <p>
for (int i = currentTocReferences.size(); i <= pathElements[pathPos]; i++) { * Example:
String sectionTitle = createSectionTitle(pathElements, pathPos, i, * Calling this method with a Resource and new int[] {0, 0} will result in the following:
sectionPrefix, * <ul>
sectionNumberSeparator); * <li>a TOCReference at the root level.<br/>
currentTocReferences.add(new TOCReference(sectionTitle, null)); * If this TOCReference did not yet exist it will have been created with a title of "" and does not point to any resource</li>
* <li>A TOCReference that points to the given resource and is a child of the previously created TOCReference.<br/>
* If this TOCReference didn't exist yet it will be created and have a title of ""</li>
* </ul>
*
* @param resource resource
* @param pathElements pathElements
* @return the new TOCReference
*/
@SuppressWarnings("unused")
public TOCReference addSection(Resource resource, int[] pathElements,
String sectionTitlePrefix, String sectionNumberSeparator) {
if (pathElements == null || pathElements.length == 0) {
return null;
}
TOCReference result = null;
List<TOCReference> currentTocReferences = this.tocReferences;
for (int i = 0; i < pathElements.length; i++) {
int currentIndex = pathElements[i];
if (currentIndex > 0 && currentIndex < (currentTocReferences.size()
- 1)) {
result = currentTocReferences.get(currentIndex);
} else {
result = null;
}
if (result == null) {
paddTOCReferences(currentTocReferences, pathElements, i,
sectionTitlePrefix, sectionNumberSeparator);
result = currentTocReferences.get(currentIndex);
}
currentTocReferences = result.getChildren();
}
result.setResource(resource);
return result;
} }
}
private String createSectionTitle(int[] pathElements, int pathPos, private void paddTOCReferences(List<TOCReference> currentTocReferences,
int lastPos, int[] pathElements, int pathPos, String sectionPrefix,
String sectionPrefix, String sectionNumberSeparator) { String sectionNumberSeparator) {
StringBuilder title = new StringBuilder(sectionPrefix); for (int i = currentTocReferences.size(); i <= pathElements[pathPos]; i++) {
for (int i = 0; i < pathPos; i++) { String sectionTitle = createSectionTitle(pathElements, pathPos, i,
if (i > 0) { sectionPrefix,
title.append(sectionNumberSeparator); sectionNumberSeparator);
} currentTocReferences.add(new TOCReference(sectionTitle, null));
title.append(pathElements[i] + 1); }
} }
if (pathPos > 0) {
title.append(sectionNumberSeparator); private String createSectionTitle(int[] pathElements, int pathPos,
int lastPos,
String sectionPrefix, String sectionNumberSeparator) {
StringBuilder title = new StringBuilder(sectionPrefix);
for (int i = 0; i < pathPos; i++) {
if (i > 0) {
title.append(sectionNumberSeparator);
}
title.append(pathElements[i] + 1);
}
if (pathPos > 0) {
title.append(sectionNumberSeparator);
}
title.append(lastPos + 1);
return title.toString();
} }
title.append(lastPos + 1);
return title.toString();
}
public TOCReference addTOCReference(TOCReference tocReference) { public TOCReference addTOCReference(TOCReference tocReference) {
if (tocReferences == null) { if (tocReferences == null) {
tocReferences = new ArrayList<>(); tocReferences = new ArrayList<>();
}
tocReferences.add(tocReference);
return tocReference;
} }
tocReferences.add(tocReference);
return tocReference;
}
/** /**
* All unique references (unique by href) in the order in which they are referenced to in the table of contents. * All unique references (unique by href) in the order in which they are referenced to in the table of contents.
* *
* @return All unique references (unique by href) in the order in which they are referenced to in the table of contents. * @return All unique references (unique by href) in the order in which they are referenced to in the table of contents.
*/ */
public List<Resource> getAllUniqueResources() { public List<Resource> getAllUniqueResources() {
Set<String> uniqueHrefs = new HashSet<>(); Set<String> uniqueHrefs = new HashSet<>();
List<Resource> result = new ArrayList<>(); List<Resource> result = new ArrayList<>();
getAllUniqueResources(uniqueHrefs, result, tocReferences); getAllUniqueResources(uniqueHrefs, result, tocReferences);
return result; return result;
} }
private static void getAllUniqueResources(Set<String> uniqueHrefs, private static void getAllUniqueResources(
List<Resource> result, List<TOCReference> tocReferences) { Set<String> uniqueHrefs,
for (TOCReference tocReference : tocReferences) { List<Resource> result,
Resource resource = tocReference.getResource(); List<TOCReference> tocReferences
if (resource != null && !uniqueHrefs.contains(resource.getHref())) { ) {
uniqueHrefs.add(resource.getHref()); for (TOCReference tocReference : tocReferences) {
result.add(resource); Resource resource = tocReference.getResource();
} if (resource != null && !uniqueHrefs.contains(resource.getHref())) {
getAllUniqueResources(uniqueHrefs, result, tocReference.getChildren()); uniqueHrefs.add(resource.getHref());
result.add(resource);
}
getAllUniqueResources(uniqueHrefs, result, tocReference.getChildren());
}
} }
}
/** /**
* The total number of references in this table of contents. * The total number of references in this table of contents.
* *
* @return The total number of references in this table of contents. * @return The total number of references in this table of contents.
*/ */
public int size() { public int size() {
return getTotalSize(tocReferences); return getTotalSize(tocReferences);
} }
private static int getTotalSize(Collection<TOCReference> tocReferences) { private static int getTotalSize(Collection<TOCReference> tocReferences) {
int result = tocReferences.size(); int result = tocReferences.size();
for (TOCReference tocReference : tocReferences) { for (TOCReference tocReference : tocReferences) {
result += getTotalSize(tocReference.getChildren()); result += getTotalSize(tocReference.getChildren());
}
return result;
} }
return result;
}
/** /**
* The maximum depth of the reference tree * The maximum depth of the reference tree
* @return The maximum depth of the reference tree *
*/ * @return The maximum depth of the reference tree
public int calculateDepth() { */
return calculateDepth(tocReferences, 0); public int calculateDepth() {
} return calculateDepth(tocReferences, 0);
}
private int calculateDepth(List<TOCReference> tocReferences, private int calculateDepth(List<TOCReference> tocReferences,
int currentDepth) { int currentDepth) {
int maxChildDepth = 0; int maxChildDepth = 0;
for (TOCReference tocReference : tocReferences) { for (TOCReference tocReference : tocReferences) {
int childDepth = calculateDepth(tocReference.getChildren(), 1); int childDepth = calculateDepth(tocReference.getChildren(), 1);
if (childDepth > maxChildDepth) { if (childDepth > maxChildDepth) {
maxChildDepth = childDepth; maxChildDepth = childDepth;
} }
}
return currentDepth + maxChildDepth;
} }
return currentDepth + maxChildDepth;
}
} }

Loading…
Cancel
Save