Create PDF and print with a WIFI printer in android kotlin.

Harshita Bambure
6 min readJun 16, 2022

Today we will learn how to create a pdf file with the itext7 library and after completing a pdf file we will be able to print this pdf using a wifi printer.

In build. gradle we need to add the dependency.

build.Gradle(:app)

implementation 'com.itextpdf:io:7.0.2'
implementation 'com.itextpdf:kernel:7.0.2'
implementation 'com.itextpdf:layout:7.0.2'
implementation 'com.karumi:dexter:6.2.3'

In the android manifest, we need to add permission as well as we need to add file provider.

Android Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.harshita.pdfprintwithwifiprinter">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PDFprintWithWIFIprinter"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.harshita.pdfprintwithwifiprinter.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>

</application>

</manifest>

In the res folder, we need to add an XML directory and after creating the XML directory generate an XML file.

provider_path.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="."/>
</paths>

Now we will design our UI part in the main activity.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">

<Button
android:id="@+id/btn_cPDF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Print PDF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now we will code for the MainActivity.

MainActivity.kt

package com.harshita.pdfprintwithwifiprinter

import android.content.Context
import android.os.Bundle
import android.print.PrintAttributes
import android.print.PrintManager
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.harshita.pdfprintwithwifiprinter.databinding.ActivityMainBinding
import com.itextpdf.kernel.geom.PageSize
import com.itextpdf.kernel.pdf.PdfDocument
import com.itextpdf.kernel.pdf.PdfWriter
import com.itextpdf.layout.Document
import com.itextpdf.layout.border.Border
import com.itextpdf.layout.element.*
import com.itextpdf.layout.property.TabAlignment
import com.itextpdf.layout.property.TextAlignment
import com.itextpdf.layout.property.UnitValue
import com.karumi.dexter.Dexter
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.single.PermissionListener
import java.io.File
import java.io.FileNotFoundException
import java.text.SimpleDateFormat
import java.util.*


class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

val file_name : String = "text_pdf.pdf"
private val currentDateandTime: String? = null
private var todayDate: String? = null
private var currentTime: String? = null
var isFromPrinting = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)

Dexter.withContext(this)
.withPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object:PermissionListener{
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
binding.btnCPDF.setOnClickListener{
File(Comman.getAppPath(this@MainActivity)).mkdirs()
createPDFFile(Comman.getAppPath(this@MainActivity)+file_name)
}
}

override fun onPermissionDenied(p0: PermissionDeniedResponse?) {

}

override fun onPermissionRationaleShouldBeShown(
p0: PermissionRequest?,
p1: PermissionToken?
) {

}

})
.check()


/*---- FORMAT TODAY DATE ----*/
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.US)
val date = Date()
todayDate = formatter.format(date)


/*------- CURRENT TIME -----------*/
val timeFormatter = SimpleDateFormat("HH:mm", Locale.US)
val time = Calendar.getInstance().time
currentTime = timeFormatter.format(time)
}


fun getCell(textLeft: String?, alignment: TextAlignment?): Cell? {
val cell = Cell().add(Paragraph(textLeft))
cell.setPadding(0f)
cell.setTextAlignment(alignment)
cell.setBorder(Border.NO_BORDER)
return cell
}

private fun createPDFFile(path: String) {
if (File(path).exists())
File(path).delete()
try {
val PdfWriter = PdfWriter(path)

//Initialize PDF document
val pdf = PdfDocument(PdfWriter)

// Initialize document
val document = Document(pdf, PageSize.A4)
val tableu = Table(2)
tableu.addCell(getCell(" Date & Time : $currentDateandTime", TextAlignment.LEFT))

tableu.addCell(getCell(" Dated $todayDate", TextAlignment.RIGHT))
tableu.addCell(getCell(" Ref. No. : " + "", TextAlignment.LEFT))

document.add(tableu)
val tablex = Table(1)
tablex.addCell(getCell("\n Tax Invoice \n", TextAlignment.CENTER))
document.add(tablex)
val tables = Table(2)
tables.addCell(getCell(" No. of boxes: 10", TextAlignment.CENTER))
tables.addCell(getCell(" Address : xyz " , TextAlignment.LEFT))
tables.addCell(getCell(" Builty no. : ", TextAlignment.CENTER))
tables.addCell(getCell(" ", TextAlignment.LEFT))
tables.addCell(getCell(" Dispatch date: 14/06/2022", TextAlignment.CENTER))
tables.addCell(getCell(" ", TextAlignment.LEFT))
tables.addCell(getCell(" Transport: Truck", TextAlignment.CENTER))
tables.addCell(getCell(" GSTIN/UIN : 123456789 ", TextAlignment.LEFT))
tables.addCell(getCell(" Destination: India", TextAlignment.CENTER))
tables.addCell(getCell(" State Name : Maharashtra ", TextAlignment.LEFT))
document.add(tables)
val table2 = Table(
UnitValue.createPercentArray(
floatArrayOf(
1f,
4f,
3f,
3f,
3f,
1f,
3f
)
)
).useAllAvailableWidth()

// table2 ...01
table2.addCell(Cell().add(Paragraph("Sr No. ")))
table2.addCell(Cell().add(Paragraph("Description of Goods")))
table2.addCell(Cell().add(Paragraph("HSN/SAC")))
table2.addCell(Cell().add(Paragraph("Quantity ")))
table2.addCell(Cell().add(Paragraph("Rate ")))
table2.addCell(Cell().add(Paragraph("Per ")))
table2.addCell(Cell().add(Paragraph("Amount ")))
var totalAmount = 0.0
var totalQty = 0.0
var index = 0

val dt = String.format("%.2f", totalAmount)
table2.addCell(Cell().add(Paragraph("1")))
table2.addCell(Cell().add(Paragraph("Total")))
table2.addCell(Cell().add(Paragraph("5265")))
table2.addCell(Cell().add(Paragraph(totalQty.toString())))
table2.addCell(Cell().add(Paragraph("50")))
table2.addCell(Cell().add(Paragraph("2")))
table2.addCell(Cell().add(Paragraph(java.lang.Double.valueOf(dt).toString())))

//table2 ...5
document.add(table2)
//Middle text
val pg = Paragraph("Amount Chargeable (in words) \n")
pg.add(NumberToWord.convert(totalAmount.toLong()).toString() + " only.")
pg.add(Tab())
pg.addTabStops(TabStop(1000F, TabAlignment.RIGHT))
pg.add("E. & O.E")
document.add(pg)


val p = Paragraph("")
p.add("\n")
p.add("\n Declaration \n")
p.add("We declare that this invoice shows the actual \n price of the goods described and that all particulars \n are true and correct")
p.add("\n")
p.add(Tab())
p.addTabStops(TabStop(1000F, TabAlignment.RIGHT))
p.add("For Harshita Bambure \n")
document.add(p)
val tableb = Table(1)
tableb.addCell(getCell("\n Authorised Signnatory", TextAlignment.RIGHT))
// tableb.addCell(getCell("This is computer generated Invoice",TextAlignment.CENTER));
document.add(tableb)
val tablem = Table(1)
tablem.addCell(getCell("\n This is computer generated Invoice", TextAlignment.CENTER))
document.add(tablem)
val tablelow = Table(2)
tablelow.addCell(getCell("\n Printed by: A", TextAlignment.LEFT))
document.add(tablelow)
document.close()
Toast.makeText(this, "Sucess", Toast.LENGTH_SHORT).show()
printPDF()
isFromPrinting = true
} catch (e: FileNotFoundException) {
e.printStackTrace()
}
}


private fun printPDF() {
val printManager = getSystemService(Context.PRINT_SERVICE) as PrintManager

try {
val printAdapter = PdfDocumentAdapter(this,Comman.getAppPath(this)+file_name)
printManager.print("Documents",printAdapter,PrintAttributes.Builder().build())
}catch (e:Exception){
Log.e("Harshita",""+e.message)
}

}
}

Common. kt

package com.harshita.pdfprintwithwifiprinter

import android.content.Context
import java.io.File

object Comman {

fun getAppPath(context: Context): String? {
val dir = File(
context.filesDir
.toString() + File.separator
+ context.resources.getString(R.string.app_name)
+ File.separator
)
if (!dir.exists()) dir.mkdir()
return dir.path + File.separator
}
}

PdfDocumentAdapter.kt

package com.harshita.pdfprintwithwifiprinter

import android.content.Context
import android.os.Bundle
import android.os.CancellationSignal
import android.os.ParcelFileDescriptor
import android.print.PageRange
import android.print.PrintAttributes
import android.print.PrintDocumentAdapter
import android.print.PrintDocumentInfo
import android.util.Log
import java.io.*


class PdfDocumentAdapter(var context: Context, var path: String) :
PrintDocumentAdapter() {
override fun onLayout(
oldAttributes: PrintAttributes,
printAttributes1: PrintAttributes,
cancellationSignal: CancellationSignal,
layoutResultCallback: LayoutResultCallback,
extras: Bundle
) {
if (cancellationSignal.isCanceled) layoutResultCallback.onLayoutCancelled() else {
val builder = PrintDocumentInfo.Builder("file name")
builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
.build()
layoutResultCallback.onLayoutFinished(
builder.build(),
printAttributes1 != printAttributes1
)
}
}

override fun onWrite(
pages: Array<PageRange>,
parcelFileDescriptor: ParcelFileDescriptor,
cancellationSignal: CancellationSignal,
writeResultCallback: WriteResultCallback
) {
var `in`: InputStream? = null
var out: OutputStream? = null
try {
val file = File(path)
`in` = FileInputStream(file)
out = FileOutputStream(parcelFileDescriptor.fileDescriptor)
val buff = ByteArray(16384)
var size: Int
while (`in`.read(buff).also { size = it } >= 0 && !cancellationSignal.isCanceled) {
out.write(buff, 0, size)
}
if (cancellationSignal.isCanceled) writeResultCallback.onWriteCancelled() else {
writeResultCallback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
}
} catch (e: Exception) {
writeResultCallback.onWriteFailed(e.message)
Log.e("Harshita", e.message!!)
e.printStackTrace()
} finally {
try {
`in`!!.close()
out!!.close()
} catch (ex: IOException) {
Log.e("Harshita", "" + ex.message)
}
}
}
}

This Number to word file converts the number into the word.

NumberToWord.kt

package com.harshita.pdfprintwithwifiprinter

import com.harshita.pdfprintwithwifiprinter.NumberToWord
import java.text.DecimalFormat
import kotlin.jvm.JvmStatic

object NumberToWord {
private val tensNames = arrayOf(
"",
" ten",
" twenty",
" thirty",
" forty",
" fifty",
" sixty",
" seventy",
" eighty",
" ninety"
)
private val numNames = arrayOf(
"",
" one",
" two",
" three",
" four",
" five",
" six",
" seven",
" eight",
" nine",
" ten",
" eleven",
" twelve",
" thirteen",
" fourteen",
" fifteen",
" sixteen",
" seventeen",
" eighteen",
" nineteen"
)

private fun convertLessThanOneThousand(number: Int): String {
var number = number
var soFar: String
if (number % 100 < 20) {
soFar = numNames[number % 100]
number /= 100
} else {
soFar = numNames[number % 10]
number /= 10
soFar = tensNames[number % 10] + soFar
number /= 10
}
return if (number == 0) soFar else numNames[number] + " hundred" + soFar
}

fun convert(number: Long): String {
// 0 to 999 999 999 999
if (number == 0L) {
return "zero"
}
var snumber = java.lang.Long.toString(number)

// pad with "0"
val mask = "000000000000"
val df = DecimalFormat(mask)
snumber = df.format(number)

// XXXnnnnnnnnn
val billions = snumber.substring(0, 3).toInt()
// nnnXXXnnnnnn
val millions = snumber.substring(3, 6).toInt()
// nnnnnnXXXnnn
val hundredThousands = snumber.substring(6, 9).toInt()
// nnnnnnnnnXXX
val thousands = snumber.substring(9, 12).toInt()
val tradBillions: String
tradBillions = when (billions) {
0 -> ""
1 -> convertLessThanOneThousand(billions)+" billion "
else -> convertLessThanOneThousand(billions)+" billion "
}
var result = tradBillions
val tradMillions: String
tradMillions = when (millions) {
0 -> ""
1 -> convertLessThanOneThousand(millions)+" million "
else -> convertLessThanOneThousand(millions)+" million "
}
result = result + tradMillions
val tradHundredThousands: String
tradHundredThousands = when (hundredThousands) {
0 -> ""
1 -> "one thousand "
else -> convertLessThanOneThousand(hundredThousands)+" thousand "
}
result = result + tradHundredThousands
val tradThousand: String
tradThousand = convertLessThanOneThousand(thousands)
result = result + tradThousand

// remove extra spaces!
return result.replace("^\\s+".toRegex(), "").replace("\\b\\s{2,}\\b".toRegex(), " ")
}

/**
* testing
*
* @param args
*/
@JvmStatic
fun main(args: Array<String>) {
println("*** " + convert(0))
println("*** " + convert(1))
println("*** " + convert(16))
println("*** " + convert(100))
println("*** " + convert(118))
println("*** " + convert(200))
println("*** " + convert(219))
println("*** " + convert(800))
println("*** " + convert(801))
println("*** " + convert(1316))
println("*** " + convert(1000000))
println("*** " + convert(2000000))
println("*** " + convert(3000200))
println("*** " + convert(700000))
println("*** " + convert(9000000))
println("*** " + convert(9001000))
println("*** " + convert(123456789))
println("*** " + convert(2147483647))
println("*** " + convert(3000000010L))
}
}

--

--

Harshita Bambure

Android Developer || WomenTech Global Ambassador at WomenTech Network. || Yoga Teacher || Member @WTM .