Parsing pdf with Kotlin using a Uri? - pdf

I write Kotlin code in Android Studio. The user chooses a file from the phone (I need to access the content as a string). There I get a Uri?. With that Uri? I can extract text from .csv and .txt files:
if (typeOfFile == ".txt" || typeOfFile == ".csv") {
try {
val ins: InputStream? = contentResolver?.openInputStream(uriFromSelectedFile)
val reader = BufferedReader(ins!!.reader())
textIWant = reader.readText()
...
Getting the file type also works fine, but when it comes to opening pdf files, nothing seems to work. I tried using PDFBox from Apache in various ways. The pdf I try to open is a simple onePager and contains only extractable text (can be copied) like this pdf.
This is one of the things I tried, the phone freezes when the file to open is a pdf:
if (typeOfFile == ".pdf") {
try {
val myPDDocument:PDDocument = PDDocument(COSDocument(ScratchFile(File(uriFromSelectedFile.path))))
textIWant = PDFTextStripper().getText(myPDDocument)
...
I´ve been trying for days. Does anyone know, how it works in Kotlin?

It worked using tom_roush.pdfbox and a companion object:
import com.tom_roush.pdfbox.text.PDFTextStripper
class MainActivity : AppCompatActivity() {
companion object PdfParser {
fun parse(fis: InputStream): String {
var content = ""
com.tom_roush.pdfbox.pdmodel.PDDocument.load(fis).use { pdfDocument ->
if (!pdfDocument.isEncrypted) {
content = PDFTextStripper().getText(pdfDocument)
}
}
return content
}
}
Calling the parse function of the companion object:
val fis: InputStream = contentResolver?.openInputStream(uriFromSelectedFile)!!
textIWant = parse(fis)

Related

File picker doesnt get the path right

im using the modern way to get user to pick file and then get it using the "registerForActivityResult(ActivityResultContracts.StartActivityForResult())" method, but the path that I get is not correct (after creating File with the given path it says it doesnt exists). Could anyone please help me with this problem? cant find any solution online with the modern way.
private fun initializeImportResultLauncher() {
importResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val path = result.data!!.data!!.path ?: return#registerForActivityResult
val file = File(path)
val fileExists = file.exists()
binding.importedFileText.text =
String.format("selected file: %s", importFile!!.name)
}
}
}
private fun initializeImportButton() {
binding.importButton.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
intent.addCategory(Intent.CATEGORY_OPENABLE)
importResultLauncher.launch(intent)
}
}
in the debugger after selecting existing file it says that fileExists is false.
Path in the debugger is: /document/raw:/storage/emulated/0/Download/importFile.csv.
Or maybe im just not correctly saving files in the phone directory, if the code is correct could I get some help with that.
Tried every other method but just cant get it working

Passing Multiple Mime Types to ActivityResultLauncher.launch()

I have following code
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
//Some code here..
}
and somewhere else ,
getContent.launch("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
I can successfully select the docx file . I need to select either pdf or doc or text or docx rather just to be able to select one kind(here docx).
I would recommend using OpenDocument instead of GetContent.
val documentPick =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
// do something
}
While launching the intent just add the mime types you want to get
documentPick.launch(
arrayOf(
"application/pdf",
"application/msword",
"application/ms-doc",
"application/doc",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/plain"
)
)
Using array doesn't work in my case. Instead, the following worked correctly.
Here custom class of ActivityResultContracts.GetContent is used. fun "createIntent" is overrided to customize method to make intent from the input.
// custom class of GetContent: input string has multiple mime types divided by ";"
// Here multiple mime type are divided and stored in array to pass to putExtra.
// super.createIntent creates ordinary intent, then add the extra.
class GetContentWithMultiFilter:ActivityResultContracts.GetContent() {
override fun createIntent(context:Context, input:String):Intent {
val inputArray = input.split(";").toTypedArray()
val myIntent = super.createIntent(context, "*/*")
myIntent.putExtra(Intent.EXTRA_MIME_TYPES, inputArray)
return myIntent
}
}
// define ActivityResultLauncher to launch : using custom GetContent
val getContent=registerForActivityResult(GetContentWithMultiFilter()){uri ->
... // do something
}
// launch it
// multiple mime types are separated by ";".
val inputString="audio/mpeg;audio/x-wav;audio/wav"
getContent.launch(inputString)

XDocReport converting odt to pdf how to set proper locale

I'm trying to convert some *.odt file to *.pdf using IXDocReport.
Here is the hypothetical content of the *.odt file: ${amount?string.currency} to be paid
Here is the code I do conversion with (you can run it in kotlin REPL):
import fr.opensagres.xdocreport.converter.ConverterTypeTo
import fr.opensagres.xdocreport.converter.ConverterTypeVia
import fr.opensagres.xdocreport.converter.Options
import fr.opensagres.xdocreport.document.IXDocReport
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry
import fr.opensagres.xdocreport.template.TemplateEngineKind
import java.io.ByteArrayInputStream
import java.io.File
val options: Options = Options.getTo(ConverterTypeTo.PDF).via(ConverterTypeVia.ODFDOM)
val content: ByteArray = File("/home/sandro/tmp/report.odt").readBytes()
val templateId: String = "someId"
val registry: XDocReportRegistry = XDocReportRegistry.getRegistry()
val data: MutableMap<String, Any> = mutableMapOf("amount" to 10)
ByteArrayInputStream(content).use { input ->
val report: IXDocReport =
registry.loadReport(input, templateId, TemplateEngineKind.Freemarker, true)
val tmpFile: File = createTempFile("out", ".pdf")
tmpFile.outputStream().use { output ->
report.convert(data, options, output)
println(tmpFile.toString())
}
}
and the result is the pdf file with string $10.00 to be paid
How can I set needed locale to XDocReport during conversion so the result could be changed to other currencies correctly?
P.S. I cannot control the template itself - so please do not tell me to add <#setting locale="${bean.locale}"> or something else to the template itself. The only place I can change is the code. Thanks in advance.
P.P.S. I need to render many templates per request and need to set locale per each template.
I have never used XDocReport, but maybe this will work: https://github.com/opensagres/xdocreport/wiki/FreemarkerTemplate "How to configure Freemarker?"
Quotation from there:
To configure Freemarker with XDocReport you must get the Configuration instance. To do > that you must
create a class (ex :
fr.opensagres.xdocreport.MyFreemarkerConfiguration) which implements
fr.opensagres.xdocreport.document.discovery.ITemplateEngineInitializerDiscovery.
register with SPI this class by creating the file
META-INF/services/fr.opensagres.xdocreport.document.discovery.ITemplateEngineInitializerDiscovery
with the name of you class :
fr.opensagres.xdocreport.MyFreemarkerConfiguration This file should be
in your classpath (you can for instance host it in the
src/META-INF/services/ of your project).
So you will need a class like this:
public class MyFreemarkerConfiguration implements ITemplateEngineInitializerDiscovery {
[...]
public void initialize(ITemplateEngine templateEngine) {
if (TemplateEngineKind.Freemarker.name().equals( templateEngine.getKind())) {
Configuration cfg = ((FreemarkerTemplateEngine) templateEngine).getFreemarkerConfiguration();
cfg.setLocale(...);
}
}
}

How to show IntelliJ JSON editor in dialog?

I want to show below kind of an editor inside a dialog in an plugin that I'm developing for Kotlin and Java. I tried the below code snippet
editorTextField.setEnabled(true);
editorTextField.setOneLineMode(false);
editorTextField.setFileType(new JsonFileType());
Could someone point out how to achieve this?
Particularly I need the line numbers, JSON syntax highlighting and code folding I can see all the code specifications here. Please help me in learning how can I use them in my plugins.
JSON editor :
class JsonOutputDialog(language: Language, project: Project, text: String) : DialogWrapper(project) {
private val panel = JPanel(BorderLayout())
init {
super.setOKActionEnabled(false)
init()
val editorTextField = CustomEditorField(language, project, text)
editorTextField.setOneLineMode(false)
editorTextField.preferredSize = Dimension(800, 600)
editorTextField.isVisible = true
panel.add(editorTextField)
editorTextField.setCaretPosition(0)
}
override fun createCenterPanel() = panel
}
class CustomEditorField(language: Language, project: Project, s: String) : LanguageTextField(language, project, s) {
override fun createEditor(): EditorEx {
val editor = super.createEditor()
editor.setVerticalScrollbarVisible(true)
editor.setHorizontalScrollbarVisible(true)
val settings = editor.settings
settings.isLineNumbersShown = true
settings.isAutoCodeFoldingEnabled = true
settings.isFoldingOutlineShown = true
settings.isAllowSingleLogicalLineFolding = true
settings.isRightMarginShown=true
return editor
}
}
This is what you need to do. The key here is to use LanguageTextField instead of EditorTextField and override the createEditor() method to configure all the options that you are looking for like Line Numbers and Code Folding.

Render a PDF file and save to object using grails Rendering and Attachmentable plugins

I am attempting to generate a PDF file that contains object information and then attach it to another object that is stored in the database. The attachmentable plugin I am using is working now for user end attachments, but I need my system to be able to do it automatically.
I am using:
Grails 1.3.9
Attachmentable 0.3.0 http://grails.org/plugin/attachmentable
Rendering 0.4.3 http://grails.org/plugin/rendering
I have been able to generate and display the pdf, but do not know how to attach it using the attachmentable plugin. I need some way to take the generated pdf byte array and convert it to a MultipartFile for the attachmentable plugin function I call. The error I get shows that my argument types are invalid.
I save object1 and object2, then generate the pdf of object1 and try to attach it to object2.
Thanks in advance for you help!
Thing1 Controller Snippets:
ByteArrayOutputStream bytes = pdfRenderingService.render(template: "/thing1/pdf", model: [thing1: thing1])
attachmentableService.addAttachment("unknown", thing2.id, bytes)
AttachmentableService function I am attempting to call:
def addAttachment(def poster, def reference, CommonsMultipartFile file) {
addAttachment(CH.config, poster, reference, file)
}
def addAttachment(def config,
def poster,
def reference,
CommonsMultipartFile file) {
if (reference.ident() == null) {
throw new AttachmentableException(
"You must save the entity [${delegate}] before calling addAttachment.")
}
if (!file?.size) {
throw new EmptyFileException(file.name, file.originalFilename)
}
String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
Long posterId = (poster instanceof String) ? 0L : poster.id
String filename = file.originalFilename
// link
def link = AttachmentLink.findByReferenceClassAndReferenceId(
delegateClassName, reference.ident())
if (!link) {
link = new AttachmentLink(
referenceClass: delegateClassName,
referenceId: reference.ident())
}
// attachment
Attachment attachment = new Attachment(
// file
name: FilenameUtils.getBaseName(filename),
ext: FilenameUtils.getExtension(filename),
length: 0L,
contentType: file.contentType,
// poster
posterClass: posterClass,
posterId: posterId,
// input
inputName: file.name)
link.addToAttachments attachment
if (!link.save(flush: true)) {
throw new AttachmentableException(
"Cannot create Attachment for arguments [$user, $file], they are invalid.")
}
// save file to disk
File diskFile = AttachmentableUtil.getFile(config, attachment, true)
file.transferTo(diskFile)
attachment.length = diskFile.length()
// interceptors
if(reference.respondsTo('onAddAttachment')) {
reference.onAddAttachment(attachment)
}
attachment.save(flush:true) // Force update so searchable can try to index it again.
return reference
}
Grails runtime error:
groovy.lang.MissingMethodException: No signature of method: com.macrobit.grails.plugins.attachmentable.services.AttachmentableService.addAttachment() is applicable for argument types: (java.lang.String, java.lang.Long, java.io.ByteArrayOutputStream) values: [unknown, 80536, %PDF-1.4 and a long string of unreadable data...]
Possible solutions: addAttachment(java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile), addAttachment(java.lang.Object, java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile)
Service Method I Added:
def customAddMethod(def poster, def reference, def pdfBytes) {
customAddMethod(CH.config, poster, reference, pdfBytes)
}
def customAddMethod(def config,
def poster,
def reference,
def pdfBytes) {
if (reference.ident() == null) {
throw new AttachmentableException(
"You must save the entity [${delegate}] before calling customAddMethod.")
}
String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
Long posterId = (poster instanceof String) ? 0L : poster.id
String filename = "File Name"
// link
def link = AttachmentLink.findByReferenceClassAndReferenceId(
delegateClassName, reference.ident())
if (!link) {
link = new AttachmentLink(
referenceClass: delegateClassName,
referenceId: reference.ident())
}
// attachment
Attachment attachment = new Attachment(
// file
name: "File Name",
ext: "pdf",
length: 0L,
contentType: "application/pdf",
// poster
posterClass: posterClass,
posterId: posterId,
// input
inputName: "File Name")
link.addToAttachments attachment
if (!link.save(flush: true)) {
throw new AttachmentableException(
"Cannot create Attachment for arguments [$user, $file], they are invalid.")
}
// save file to disk
byte[] bytes = pdfBytes.toByteArray(); //convert ByteArrayOutputStream to ByteArray
File diskFile = AttachmentableUtil.getFile(config, attachment, true) //file path
FileOutputStream fos = new FileOutputStream(diskFile); //open file output stream to write to
fos.write(bytes); //write rendered pdf bytes to file
fos.flush();
fos.close();
attachment.length = diskFile.length()
// interceptors
if(reference.respondsTo('onAddAttachment')) {
reference.onAddAttachment(attachment)
}
attachment.save(flush:true) // Force update so searchable can try to index it again.
return reference
}
It looks like the AttachmentableService you referenced (from the Attachmentable plugin) assumes it's dealing with a file-upload scenario, such that you could easily grab the MultipartFile instance via request.getFile(). That's not the case for you - you're creating the file via the Rendering plugin, and you want that file attached to a domain object, right?
You could try constructing a CommonsMultipartFile instance manually by first writing the pdf bytes to disk, and then create a DiskFileItem via DiskFileItemFactory.
See this post for an example of what I'm thinking:
How to make CommonsMultipartFile from absolute file path?
Another, better, option might be to checkout that plugin's source and add a method that doesn't require you to go through those gyrations - perhaps a version of the addAttachment method that accepts a File or an OutputStream instead - and submit a PR to the plugin author.
(Looks like they're adding an 'addAttachment' method to qualifying domain objects, which also expects a CommonsMultipartFile).
Otherwise, you might just have to create your own service to basically provide the same end result, which apparently is to create an AttachmentLink and associated Attachment instance.