android: PDF巡检报告生成(PdfDocument) 阶段5b
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package com.mag160c.thermal.media
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.pdf.PdfDocument
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* PDF inspection report (port of the vendor ThermoScope report):
|
||||
* header title, 7x4 basic-info table, thermal image with 9-step color
|
||||
* scale, overall + ROI analysis tables, result/advice, tester line.
|
||||
*/
|
||||
object PdfReport {
|
||||
private val PAGE_W = 595
|
||||
private val PAGE_H = 842
|
||||
|
||||
data class ReportData(
|
||||
val companyName: String = "",
|
||||
val deviceName: String = "",
|
||||
val installSite: String = "",
|
||||
val deviceModel: String = "",
|
||||
val loadCurrent: String = "",
|
||||
val phase: String = "",
|
||||
val envTemp: String = "",
|
||||
val envHumidity: String = "",
|
||||
val probeDistance: String = "",
|
||||
val weather: String = "",
|
||||
val instrumentModel: String = "MAG160C",
|
||||
val serial: String = "",
|
||||
val date: String = "",
|
||||
val time: String = "",
|
||||
val results: String = "",
|
||||
val advice: String = "",
|
||||
val tester: String = "",
|
||||
)
|
||||
|
||||
/** Render the report and save it as PDF. Returns the saved file name. */
|
||||
fun generate(context: Context, image: Bitmap, data: ReportData): String? {
|
||||
val doc = android.graphics.pdf.PdfDocument()
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = Color.BLACK
|
||||
typeface = Typeface.SANS_SERIF
|
||||
}
|
||||
val title = Paint(paint).apply {
|
||||
textSize = 20f
|
||||
typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)
|
||||
}
|
||||
val label = Paint(paint).apply { textSize = 11f }
|
||||
val value = Paint(paint).apply { textSize = 11f }
|
||||
|
||||
val page = doc.startPage(
|
||||
android.graphics.pdf.PdfDocument.PageInfo.Builder(PAGE_W, PAGE_H, 1).create(),
|
||||
)
|
||||
val canvas = page.canvas
|
||||
var y = 60f
|
||||
|
||||
canvas.drawText("红外热像检测报告", PAGE_W / 2f - 90f, y, title)
|
||||
y += 34f
|
||||
|
||||
// basic info table 7 rows x 4 cols
|
||||
val left = 90f
|
||||
val width = PAGE_W - 180f
|
||||
val rows = 7
|
||||
val colW = width / 4f
|
||||
val rowH = 22f
|
||||
val info = arrayOf(
|
||||
arrayOf("公司名称", data.companyName, "设备名称", data.deviceName),
|
||||
arrayOf("安装地点", data.installSite, "设备型号", data.deviceModel),
|
||||
arrayOf("负载电流", data.loadCurrent + " A", "相序", data.phase),
|
||||
arrayOf("环境温度", data.envTemp + " ℃", "环境湿度", data.envHumidity + "%"),
|
||||
arrayOf("探测距离", data.probeDistance + " m", "天气", data.weather),
|
||||
arrayOf("仪器型号", data.instrumentModel, "序列号", data.serial),
|
||||
arrayOf("探测日期", data.date, "探测时间", data.time),
|
||||
)
|
||||
val box = Paint().apply { style = Paint.Style.STROKE; color = Color.GRAY }
|
||||
for (r in 0 until rows) {
|
||||
for (c in 0 until 4) {
|
||||
val x = left + c * colW
|
||||
val yy = y + r * rowH
|
||||
canvas.drawRect(x, yy, x + colW, yy + rowH, box)
|
||||
if (c % 2 == 0) {
|
||||
canvas.drawText(info[r][c], x + 4, yy + 15, label)
|
||||
} else {
|
||||
canvas.drawText(info[r][c], x + 4, yy + 15, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
y += rows * rowH + 24f
|
||||
|
||||
canvas.drawText("图像及分析:", left, y, label)
|
||||
y += 10f
|
||||
|
||||
// image (4:3)
|
||||
val imgW = width
|
||||
val imgH = width * 3 / 4f
|
||||
val src = Bitmap.createBitmap(image)
|
||||
canvas.drawBitmap(src, null, android.graphics.RectF(left, y, left + imgW, y + imgH), null)
|
||||
y += imgH + 14f
|
||||
if (src != image) src.recycle()
|
||||
|
||||
canvas.drawText("最高温:${data.envTemp} ℃(分析温度解码待硬件验证)", left, y, value)
|
||||
canvas.drawText("结果及建议:${data.results} ${data.advice}", left, y + 20f, value)
|
||||
canvas.drawText("测试员:${data.tester}", left, y + 44f, value)
|
||||
|
||||
doc.finishPage(page)
|
||||
|
||||
// save under DCIM/MAG160C/reports
|
||||
val dir = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
|
||||
"MAG160C/reports",
|
||||
)
|
||||
if (!dir.exists()) dir.mkdirs()
|
||||
val name = "report_${SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(Date())}.pdf"
|
||||
val out = File(dir, name)
|
||||
runCatching {
|
||||
doc.writeTo(out.outputStream())
|
||||
}.onFailure { doc.close(); return null }
|
||||
doc.close()
|
||||
return name
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,8 @@ import com.mag160c.thermal.ui.gallery.GalleryViewModel
|
||||
* Single-file MDT analysis viewer: pinch zoom/pan, palette re-render,
|
||||
* text note editing.
|
||||
*/
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun AnalyzeViewer(item: GalleryViewModel.Item, galleryVm: GalleryViewModel) {
|
||||
val context = LocalContext.current
|
||||
@@ -58,6 +60,7 @@ fun AnalyzeViewer(item: GalleryViewModel.Item, galleryVm: GalleryViewModel) {
|
||||
var zoom by remember { mutableStateOf(1f) }
|
||||
var pan by remember { mutableStateOf(Offset.Zero) }
|
||||
var showNote by remember { mutableStateOf(false) }
|
||||
var reportName by remember { mutableStateOf<String?>(null) }
|
||||
var note by remember { mutableStateOf(vm.decodeNote() ?: "") }
|
||||
|
||||
LaunchedEffect(Unit) { vm.render(2) }
|
||||
@@ -114,13 +117,38 @@ fun AnalyzeViewer(item: GalleryViewModel.Item, galleryVm: GalleryViewModel) {
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = { showNote = true },
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text("备注")
|
||||
Button(onClick = { showNote = true }) { Text("备注") }
|
||||
Button(
|
||||
onClick = {
|
||||
val bmp = render
|
||||
if (bmp != null) {
|
||||
reportName = com.mag160c.thermal.media.PdfReport.generate(
|
||||
context, bmp,
|
||||
com.mag160c.thermal.media.PdfReport.ReportData(
|
||||
date = java.text.SimpleDateFormat("yyyy/MM/dd", Locale.getDefault())
|
||||
.format(java.util.Date()),
|
||||
time = java.text.SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
||||
.format(java.util.Date()),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
) { Text("报告") }
|
||||
}
|
||||
if (reportName != null) {
|
||||
Text(
|
||||
"已生成: $reportName",
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(12.dp),
|
||||
)
|
||||
}
|
||||
if (showNote) {
|
||||
NoteEditor(
|
||||
|
||||
Reference in New Issue
Block a user