您要查找的内容称为linearization 和this answer。
%PDF-1.x 标题行之后的第一个对象应
包含指示 /Linearized 属性的字典键
文件。
这种整体结构允许符合要求的读者学习
对象地址的完整列表非常快速,无需
从头到尾下载完整的文件:
您可以使用this native library 到linearization PDF。
但是
我不建议让它渲染 PDF 不会快速、流畅或感觉原生。由于这些原因,据我所知,没有本地移动应用程序可以做到linearization。此外,您必须为 PDF 创建自己的渲染引擎,因为大多数 PDF 查看库不支持 linearization 。您应该做的是在服务器端将 PDF 中的每个单独页面转换为 HTML,并让客户端仅在需要时加载页面并缓存。我们还将单独保存 PDF 计划文本以启用搜索。这样一切都会顺利,因为资源将被延迟加载。为了实现这一点,您可以执行以下操作。
首先
在服务器端,每当您发布 PDF 时,PDF 的页面都应该被拆分为 HTML 文件,如上所述。页面缩略图也应该从这些页面生成。假设你的服务器在python 上运行,flask microframework 这就是你要做的。
from flask import Flask,request
from werkzeug import secure_filename
import os
from pyPdf import PdfFileWriter, PdfFileReader
import imgkit
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
import io
import sqlite3
import Image
app = Flask(__name__)
@app.route('/publish',methods=['GET','POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
filePath = "pdfs/"+secure_filename(f.filename)
f.save(filePath)
savePdfText(filePath)
inputpdf = PdfFileReader(open(filePath, "rb"))
for i in xrange(inputpdf.numPages):
output = PdfFileWriter()
output.addPage(inputpdf.getPage(i))
with open("document-page%s.pdf" % i, "wb") as outputStream:
output.write(outputStream)
imgkit.from_file("document-page%s.pdf" % i, "document-page%s.jpg" % i)
saveThum("document-page%s.jpg" % i)
os.system("pdf2htmlEX --zoom 1.3 pdf/"+"document-page%s.pdf" % i)
def saveThum(infile):
save = 124,124
outfile = os.path.splitext(infile)[0] + ".thumbnail"
if infile != outfile:
try:
im = Image.open(infile)
im.thumbnail(size, Image.ANTIALIAS)
im.save(outfile, "JPEG")
except IOError:
print("cannot create thumbnail for '%s'" % infile)
def savePdfText(data):
fp = open(data, 'rb')
rsrcmgr = PDFResourceManager()
retstr = io.StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)
# Process each page contained in the document.
db = sqlite3.connect("pdfText.db")
cursor = db.cursor()
cursor.execute('create table if not exists pagesTextTables(id INTEGER PRIMARY KEY,pageNum TEXT,pageText TEXT)')
db.commit()
pageNum = 1
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
data = retstr.getvalue()
cursor.execute('INSERT INTO pagesTextTables(pageNum,pageText) values(?,?) ',(str(pageNum),data ))
db.commit()
pageNum = pageNum+1
@app.route('/page',methods=['GET','POST'])
def getPage():
if request.method == 'GET':
page_num = request.files['page_num']
return send_file("document-page%s.html" % page_num, as_attachment=True)
@app.route('/thumb',methods=['GET','POST'])
def getThum():
if request.method == 'GET':
page_num = request.files['page_num']
return send_file("document-page%s.thumbnail" % page_num, as_attachment=True)
@app.route('/search',methods=['GET','POST'])
def search():
if request.method == 'GET':
query = request.files['query ']
db = sqlite3.connect("pdfText.db")
cursor = db.cursor()
cursor.execute("SELECT * from pagesTextTables Where pageText LIKE '%"+query +"%'")
result = cursor.fetchone()
response = Response()
response.headers['queryResults'] = result
return response
这里解释了烧瓶应用程序正在做什么。
-
/publish 路由负责杂志的发布、将页面转换为 HTML、将 PDF 文本保存到 SQlite 数据库并为这些页面生成缩略图。我使用 pyPDF 将 PDF 拆分为单个页面,pdfToHtmlEx 将页面转换为 HTML,imgkit 将这些 HTML 生成为图像,PIL 从这些图像生成拇指。此外,一个简单的Sqlite db 可以保存页面的文本。
-
/page、/thumb 和 /search 路由是不言自明的。它们只是返回 HTML、拇指或搜索查询结果。
其次,在客户端,您只需在用户滚动到它时下载 HTML 页面。让我给你一个安卓操作系统的例子。首先,您需要创建一些 Utils 来处理 GET 请求者
public static byte[] GetPage(int mPageNum){
return CallServer("page","page_num",Integer.toString(mPageNum))
}
public static byte[] GetThum(int mPageNum){
return CallServer("thumb","page_num",Integer.toString(mPageNum))
}
private static byte[] CallServer(String route,String requestName,String requestValue) throws IOException{
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
MultipartBody.Builder mMultipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart(requestName,requestValue);
RequestBody mRequestBody = mMultipartBody.build();
Request request = new Request.Builder()
.url("yourUrl/"+route).post(mRequestBody)
.build();
Response response = client.newCall(request).execute();
return response.body().bytes();
}
上面的 helper utils 为您简单地处理对服务器的查询,它们应该是不言自明的。
接下来,您只需创建一个带有 WebView viewHolder 或更好的 advanced webview 的 RecyclerView,因为它将为您提供更多的自定义功能。
public static class ViewHolder extends RecyclerView.ViewHolder {
private AdvancedWebView mWebView;
public ViewHolder(View itemView) {
super(itemView);
mWebView = (AdvancedWebView)itemView;}
}
private class ContentAdapter extends RecyclerView.Adapter<YourFrament.ViewHolder>{
@Override
public ViewHolder onCreateViewHolder(ViewGroup container, int viewType) {
return new ViewHolder(new AdvancedWebView(container.getContext()));
}
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public void onBindViewHolder( ViewHolder holder, int position) {
handlePageDownload(holder.mWebView);
}
private void handlePageDownload(AdvancedWebView mWebView){....}
@Override
public int getItemCount() {
return numberOfPages;
}
}
应该是这样的。