То, что вы ищете, называется linearization
и согласно этот ответ .
Первый объект сразу после строки заголовка% PDF-1.x должен
содержит ключ словаря, указывающий / Linearized свойство
файл.
Эта общая структура позволяет читателю, изучающему
полный список адресов объектов очень быстро, без необходимости
загрузить полный файл от начала до конца:
Зритель может отображать первые страницы очень быстро, до
полный файл загружен.
Пользователь может щелкнуть предварительный просмотр миниатюрной страницы (или ссылку в ToC
файла), чтобы перейти, скажем, на страницу 445, сразу после
первые страницы были показаны, и зритель может запросить все
объекты, требуемые для страницы 445, запрашивая удаленный сервер через байт
диапазон запросов, чтобы доставить их «не в порядке», чтобы зритель мог
показать эту страницу быстрее. (Пока пользователь читает страницы не по порядку,
загрузка полного документа все еще будет продолжаться в
фон ...)
Вы можете использовать эту нативную библиотеку до 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-страницу всякий раз, когда пользователь прокручивает ее. Позвольте привести пример с ОС Android. Во-первых, вы хотите создать 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();
}
Вспомогательные утилиты выше просто обрабатывают запросы к серверу для вас, они должны быть самоочевидными.
Затем вы просто создаете RecyclerView
с помощью ViewHolder WebView или, что еще лучше, расширенный веб-просмотр , поскольку это даст вам больше возможностей при настройке.
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;
}
}
Это должно быть об этом.