Hi I am trying to create an A4 PDF from a view, following this resource . I have been able to successfully create the pdf but it is cutting the bottom of my view off. I have made several adjustments to the view size and adjusted all the measure functionality but still cant seem to inflate this view with independance from the screen. I believe my issues lie somewhere in this code.
/**
* Entry point for creating PDF
*
* @param context (app context)
* @param activity
*/
fun createPdf(
context: Context,
activity: Activity
) {
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.layout_pdf_page_one, null)
val bitmap = createBitmapFromView(context, view, activity, 1)
convertBitmapToPdf(bitmap, activity, nPage = 1)
}
private fun createBitmapFromView(
context: Context,
view: View,
activity: Activity,
page: Int
): Bitmap {
val binding = LayoutPdfPageOneBinding.bind(view)
binding.lifecycleOwner = binding.root.findViewTreeLifecycleOwner()
initPageOne(binding, context)
return createBitmap(context, binding.root, activity, binding)
}
private fun createBitmap(
context: Context,
view: View,
activity: Activity,
binding: LayoutPdfPageOneBinding
): Bitmap {
// whats going on here is getting the display metrics I think I need the layout
size as losing some of the bottom.
val displayMetrics = DisplayMetrics()
activity.display?.getRealMetrics(displayMetrics)
displayMetrics.densityDpi
view
view.measure(
View.MeasureSpec.makeMeasureSpec(
displayMetrics.widthPixels, View.MeasureSpec.EXACTLY
),
View.MeasureSpec.makeMeasureSpec(
displayMetrics.heightPixels, View.MeasureSpec.EXACTLY
)
)
view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
val bitmap = Bitmap.createBitmap(
view.measuredWidth,
view.measuredWidth,
Bitmap.Config.ARGB_8888
)
Log.d(TAG, "createBitmap: ${binding.pdfLayoutPageOne.width}")
Log.d(TAG, "createBitmap: ${binding.pdfLayoutPageOne.height}")
val canvas = Canvas(bitmap)
view.draw(canvas)
return Bitmap.createScaledBitmap(bitmap, 595, 842, true)
}
I realise this is currently dependant on the screen but I have adjusted those variables to no success. I have also attempted to change the layouts view sizing working out the dp width and size as well as trying mm width and size. First two blocks of this can be seen here they are currently both match parent but be assured I have tried several different sizing methods.
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@ id/pdf_layout_page_one"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10sp"
android:paddingStart="24sp"
android:paddingTop="24sp"
android:paddingEnd="24sp"
android:paddingBottom="88sp">
The question is how can I make a xml of size A4 so the design looks as it would when I export and export that view successfully? Any comments very welcome. I do not really want to use an external library as I am exporting some mpandroidcharts within the view for export from bitmap to pdf.
CodePudding user response:
The problem is with the code
val displayMetrics = DisplayMetrics()
activity.display?.getRealMetrics(displayMetrics)
displayMetrics.densityDpi
view
view.measure(
View.MeasureSpec.makeMeasureSpec(
displayMetrics.widthPixels, View.MeasureSpec.EXACTLY
),
View.MeasureSpec.makeMeasureSpec(
displayMetrics.heightPixels, View.MeasureSpec.EXACTLY
)
)
view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
This sizes the view to the screen size and layouts it out to the screen size.
replace with (hopefully my Kotlin is correct I usually do this in Java):-
view.measure(
// Measure to A4 width
View.MeasureSpec.makeMeasureSpec(
595, View.MeasureSpec.EXACTLY
),
// Measure to A4 height
View.MeasureSpec.makeMeasureSpec(
842, View.MeasureSpec.EXACTLY
)
)
// Layout the view out to the measure dimensions
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight())
You might need to set/reset Layout Params as well to WRAP_CONTENT
if the view has never been drawn to the screen before (I usually create a separate view that has never been drawn to the screen to use in PDF generation)
Also not not sure why you do View -> draw to Bitmap -> draw bitmap to PDF
when
View -> draw to PDF can be done and produces a much better PDF
To do this when you val page = pdfDocument.startPage(pageInfo)
do instead
val page = pdfDocument.startPage(pageInfo)
// Draw the view direct to the PDF's Canvas instead via Bitmap
view.draw(page.canvas)
Update
More info on what I actually do.
Instead of inflating xml I just programmatically create a linearLayout to put the things I want drawn to pdf, this linearLayout is size for the PDF not the screen.
e.g.
LinearLayout linearLayout = new LinearLayout(getApplicationContext());
The before measuring it needs layout params set
e.g.
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
But I guess in your xml could be change
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@ id/pdf_layout_page_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10sp"
android:paddingStart="24sp"
android:paddingTop="24sp"
android:paddingEnd="24sp"
android:paddingBottom="88sp">