CÁCH TẠO VÀ THÊM NỘI DUNG VÀO TỆP PDF BẰNG APACHE PDFBOX TRONG ANDROID
Trong hướng dẫn trước chúng ta đã tìm hiểu cách đọc tệp PDF trong. Hôm nay, chúng ta sẽ học cách tạo một tệp PDF mới và thêm nội dung vào tệp PDF.
Nếu bạn chưa đọc hướng dẫn trước , thì bạn nên đọc để làm được bài này.
Dưới đây là ảnh chụp màn hình của ứng dụng chúng ta sẽ tạo
1. TẠO DỰ ÁN ANDROID MỚI
- Mở Android Studio
- Chuyển đến file menu
- Chọn new option
- Nhập tên project
- Nhập tên activity
- Giữ các cài đặt mặc định khác
- Click vào nút finish để tạo một dự án Android mới
2. THÊM PERMISSION
Vì việc truy cập Android storage API cần có quyền, chúng ta sẽ thêm mã này vào tệp manifest.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3. THÊM THƯ VIỆN BÊN THỨ BA ANDROID
Chúng ta sẽ thêm một số thư viện android sẽ giúp chúng ta giải quyết một số vấn đề.
Mở dự án build.gradle của bạn và thêm các thư viện bên dưới.
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.3.0-alpha02'
//loading
implementation 'com.github.d-max:spots-dialog:1.1@aar'
//RxAndroid
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.10"
//Pdf viewer
implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'
//PDFBox android
implementation 'com.tom_roush:pdfbox-android:1.8.10.1'
//File picker
implementation 'com.github.jaiselrahman:FilePicker:1.3.2'
implementation 'com.jakewharton:butterknife:10.2.1'
annotationProcessor "com.jakewharton:butterknife-compiler:10.2.1"
implementation 'com.karumi:dexter:5.0.0'
//Lombok
compileOnly 'org.projectlombok:lombok:1.18.8'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Ta đã thêm nhiều thư viện hơn như RxJava, RxAndroid, PDFViewer và ProgressView.
4. TẠO ACTIVITY MỚI
Tạo activity mới và đặt tên là WritePdfActivity hoặc bất kỳ tên nào bạn chọn.
Mở tệp bố cục xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#f3f3f3"
android:padding="12dp"
tools:context=".WritePdfActivity">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/content_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:inputType="text"
android:hint="Enter PDF content"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/create_pdf"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:textColor="@color/colorWhite"
android:layout_marginTop="12dp"
android:text="CREATE PDF"/>
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdf_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"/>
</androidx.appcompat.widget.LinearLayoutCompat>
Tệp layout chứa EditText, Button và PDFView.
Khi người dùng nhập văn bản và click vào nút tạo PDF, văn bản sẽ được trả về và được sử dụng để tạo tệp PDF mới.
5. TẠO LỚP VIEWMODEL MỚI
Tiếp theo, chúng ta sẽ tạo một lớp Java mới sẽ kế thừa từ lớp ViewModel của Android.
Chúng ta sẽ tạo một phương thức chấp nhận văn bản và đường dẫn tệp làm tham số.
Mở lớp Java đã tạo.
@Setter
@Getter
public class PDFViewModel extends ViewModel {
private static final String TAG = PDFViewModel.class.getSimpleName();
private CompositeDisposable compositeDisposable;
private MutableLiveData<Boolean> isLoading;
public PDFViewModel() {
compositeDisposable = new CompositeDisposable();
isLoading = new MutableLiveData<>(false);
}
public void writeToPDFFile(String pdfFilePath, String pdfContent){
Completable.create(new CompletableOnSubscribe() {
@Override
public void subscribe(CompletableEmitter emitter) throws Exception {
openAndWriteToPDFFile(pdfFilePath, pdfContent);
emitter.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
isLoading.setValue(true);
Log.d(TAG, "Completed");
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "Error message " + e.getLocalizedMessage());
}
});
}
private void openAndWriteToPDFFile(String pdfFilePath, String pdfContent){
PDDocument pdDocument = new PDDocument();
PDPage page = new PDPage();
pdDocument.addPage(page);
try {
PDPageContentStream contentStream = new PDPageContentStream(pdDocument, page);
contentStream.beginText();
contentStream.setFont(PDType1Font.COURIER_BOLD, 18);
contentStream.setLeading(16f);
contentStream.newLineAtOffset(30, 600);
contentStream.showText(pdfContent);
contentStream.endText();
contentStream.close();
Log.d(TAG, "File Path Log " + pdfFilePath);
pdDocument.save(new File(pdfFilePath));
} catch (IOException e) {
e.printStackTrace();
}
}
6. MỞ LỚP ACTIVITY
Trong lớp activity, chúng ta nhận được tất cả các tham chiếu đến các view trong tệp layout
Chúng ta sẽ đính kèm sự kiện onclick vào button. Sau khi người dùng nhập văn bản và click vào nút, phương thức writeToPDFFile(String pdfFilePath, String pdfContent)
được gọi.
Mã hoàn chỉnh cho activity được hiển thị bên dưới
public class WritePdfActivity extends AppCompatActivity {
private static final String TAG = WritePdfActivity.class.getSimpleName();
private File fileStorage;
private static String FILE_PATH_NAME;
private PDFViewModel viewModel;
@BindView(R.id.content_box)
AppCompatEditText contentBox;
@BindView(R.id.pdf_view)
PDFView pdfView;
private AlertDialog progressView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_pdf);
ButterKnife.bind(this);
permissionRequest();
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setTitle("Create New PDF File");
}
PDFBoxResourceLoader.init(this);
viewModel = ViewModelProviders.of(this).get(PDFViewModel.class);
progressView = new SpotsDialog.Builder().setContext(this).setCancelable(false).setMessage("Processing...").build();
fileStorage = getExternalFilesDir("PDF");
if (fileStorage != null){
if(!fileStorage.exists()){
fileStorage.mkdir();
}
FILE_PATH_NAME = fileStorage.getAbsolutePath() + File.separator + "text.pdf";
}
}
private void permissionRequest() {
Dexter.withActivity(this).withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
//do work when permission is granted
}else if (report.isAnyPermissionPermanentlyDenied()) {
Log.d(TAG, "Permission has been granted");
}
}
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
})
.withErrorListener(new PermissionRequestErrorListener() {
@Override
public void onError(DexterError error) {
Log.e(TAG, error.toString());
}
})
.onSameThread()
.check();
}
@OnClick(R.id.create_pdf)
public void createNewPDF(View view){
String content = Objects.requireNonNull(contentBox.getText()).toString();
if (TextUtils.isEmpty(content)){
Toast.makeText(this, "Content box must be filled", Toast.LENGTH_SHORT).show();
}else{
writeNewPDFContent(pdfView, content);
}
}
private void writeNewPDFContent(PDFView pdfView, String pdfContent){
progressView.show();
viewModel.writeToPDFFile(FILE_PATH_NAME, pdfContent);
viewModel.getIsLoading().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if (aBoolean){
// View the new PDF file
progressView.dismiss();
getPdfConfigurator(pdfView, FILE_PATH_NAME);
}else{
progressView.dismiss();
Log.d(TAG, "Something has gone wrong");
}
}
});
}
public void getPdfConfigurator(PDFView pdfView, String filePath) {
PDFView.Configurator configurator = null;
File file = new File(filePath);
if (file.exists()){
configurator = pdfView.fromFile(new File(filePath))
.enableDoubletap(true)
.enableSwipe(true)
.swipeHorizontal(true)
.enableAnnotationRendering(true)
.pageFitPolicy(FitPolicy.BOTH)
.fitEachPage(true)
.spacing(2);
}
if (configurator != null){
configurator.load();
}
}
}
Trong các hướng dẫn tiếp theo, chúng ta sẽ trình bày cách thao tác với PDF trong Android như hợp nhất nhiều tệp PDF, nén PDF, v.v.
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!