No description
  • Kotlin 99.4%
  • HTML 0.3%
  • Swift 0.2%
  • Dockerfile 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-29 08:32:15 +02:00
.junie/memory init 2026-07-21 20:09:01 +02:00
composeApp move partner and proposal partner types 2026-09-28 23:02:13 +02:00
gradle add translations 2026-09-15 21:09:50 +02:00
iosApp init 2026-07-21 20:09:01 +02:00
server init 2026-07-21 20:09:01 +02:00
shared extend ClassModel.md with Invoice 2026-09-29 08:32:15 +02:00
.dockerignore init 2026-07-21 20:09:01 +02:00
.gitignore wip impel E-rechnung 2026-09-24 23:12:31 +02:00
build.gradle.kts init 2026-07-21 20:09:01 +02:00
DB_README.md init 2026-07-21 20:09:01 +02:00
docker-compose.yml init 2026-07-21 20:09:01 +02:00
gradle.properties add translations 2026-09-15 21:09:50 +02:00
gradlew init 2026-07-21 20:09:01 +02:00
gradlew.bat init 2026-07-21 20:09:01 +02:00
README.md init 2026-07-21 20:09:01 +02:00
settings.gradle.kts init 2026-07-21 20:09:01 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop (JVM), Server.

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code that’s common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

  • /server is for the Ktor server application.

  • /shared is for the code that will be shared between all targets in the project. The most important subfolder is commonMain. If preferred, you can add code to the platform-specific folders here too.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run Server

To build and run the development version of the server, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :server:run
    
  • on Windows
    .\gradlew.bat :server:run
    

Build and Run Web Application

To build and run the development version of the web app, use the run configuration from the run widget in your IDE's toolbar or run it directly from the terminal:

  • for the Wasm target (faster, modern browsers):
    • on macOS/Linux
      ./gradlew :composeApp:wasmJsBrowserDevelopmentRun
      
    • on Windows
      .\gradlew.bat :composeApp:wasmJsBrowserDevelopmentRun
      
  • for the JS target (slower, supports older browsers):
    • on macOS/Linux
      ./gradlew :composeApp:jsBrowserDevelopmentRun
      
    • on Windows
      .\gradlew.bat :composeApp:jsBrowserDevelopmentRun
      

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform, Compose Multiplatform, Kotlin/Wasm…

We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel #compose-web. If you face any issues, please report them on YouTrack.


Me

how can i gennearte pdfs for proposals vor a crafts man from a templated in kotlin multi platform

AI Assistant

Die Generierung von PDFs aus Templates in einem Kotlin Multiplatform (KMP) Projekt für Handwerker-Angebote (Proposals) lässt sich am flexibelsten über einen HTML-zu-PDF-Ansatz realisieren. Da reine PDF-Bibliotheken in Kotlin Multiplatform oft plattformabhängig sind (z. B. Apache PDFBox auf JVM/Android und CoreGraphics auf iOS), ist der eleganteste Weg: Common Main: Generierung eines dynamischen HTML-Dokuments mithilfe von kotlinx.html oder einer einfachen String-Template-Engine. Platform-Specific (Expect/Actual): Konvertierung dieses HTML-Dokuments in ein PDF-Dokument unter Verwendung der jeweiligen nativen System-APIs. Hier ist ein praxisnaher Leitfaden, wie Sie das für Android und iOS umsetzen können.

  1. Datenmodell & HTML-Template im commonMain Zuerst erstellen wir im gemeinsamen Code (commonMain) eine Funktion, die Ihre Angebotsdaten (Proposal und LineItem) in ein gestaltetes HTML-Template übersetzt. Fügen Sie die Abhängigkeit zu kotlinx.html in Ihre build.gradle.kts ein (optional, Sie können auch einfache Kotlin String-Templates verwenden):``` kotlin // commonMain / build.gradle.kts dependencies { implementation("org.jetbrains.kotlinx:kotlinx-html:0.11.0") }

Erstellen Sie den Template-Generator in commonMain:``` kotlin
package de.os.mahler_app.mahler_app.service.pdf

import de.os.mahler_app.mahler_app.model.domain.proposal.Proposal
import de.os.mahler_app.mahler_app.model.domain.proposal.LineItem

object ProposalHtmlTemplate {
    fun generate(proposal: Proposal, lineItems: List<LineItem>): String {
        val itemsHtml = lineItems.joinToString("") { item ->
            """
            <tr>
                <td>${item.name}</td>
                <td style="text-align: right;">${item.amount}</td>
                <td style="text-align: right;">${String.format("%.2f", item.price)} €</td>
                <td style="text-align: right;">${String.format("%.2f", item.amount * item.price)} €</td>
            </tr>
            """.trimIndent()
        }

        val total = lineItems.sumOf { it.amount * it.price }

        return """
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                body { font-family: Arial, sans-serif; margin: 30px; color: #333; }
                h1 { color: #2C3E50; }
                .header { margin-bottom: 50px; }
                table { width: 100%; border-collapse: collapse; margin-top: 20px; }
                th, td { border-bottom: 1px solid #ddd; padding: 10px; text-align: left; }
                th { background-color: #f2f2f2; }
                .total { font-weight: bold; font-size: 1.2em; text-align: right; margin-top: 20px; }
            </style>
        </head>
        <body>
            <div class="header">
                <h1>Angebot</h1>
                <p><strong>Projekt:</strong> ${proposal.getId() ?: "N/A"}</p>
                <p>Vielen Dank für Ihre Anfrage. Gerne erstellen wir Ihnen folgendes Angebot:</p>
            </div>
            
            <table>
                <thead>
                    <tr>
                        <th>Position</th>
                        <th style="text-align: right;">Menge</th>
                        <th style="text-align: right;">Einzelpreis</th>
                        <th style="text-align: right;">Gesamt</th>
                    </tr>
                </thead>
                <tbody>
                    $itemsHtml
                </tbody>
            </table>
            
            <p class="total">Gesamtsumme: ${String.format("%.2f", total)} €</p>
        </body>
        </html>
        """.trimIndent()
    }
}
  1. Das expect Interface im commonMain Wir definieren eine plattformübergreifende Schnittstelle für den PDF-Generator:``` kotlin package de.os.mahler_app.mahler_app.service.pdf

expect class PdfGenerator { /**

  • Konvertiert das HTML in ein PDF und gibt den Dateipfad oder die Bytes zurück */ suspend fun generatePdfFromHtml(htmlContent: String, outputFileName: String): String }

 
3. Native Implementierung für Android (androidMain)
Unter Android können wir den systemeigenen Druck-Dienst (PrintAttributes und PdfDocument) zusammen mit einer unsichtbaren WebView nutzen, um aus HTML ein fehlerfreies PDF zu generieren.``` kotlin
package de.os.mahler_app.mahler_app.service.pdf

import android.content.Context
import android.print.PrintAttributes
import android.print.PrintDocumentAdapter
import android.print.PrintManager
import android.webkit.WebView
import android.webkit.WebViewClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCancellableCoroutine

actual class PdfGenerator(private val context: Context) {
    actual suspend fun generatePdfFromHtml(htmlContent: String, outputFileName: String): String = withContext(Dispatchers.Main) {
        suspendCancellableCoroutine { continuation ->
            val webView = WebView(context)
            webView.webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, url: String?) {
                    val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager
                    val jobName = "Proposal_Print_Job"
                    
                    // Wir nutzen den PrintAdapter der WebView, um das PDF zu rendern
                    val printAdapter = webView.createPrintDocumentAdapter(jobName)
                    val printAttributes = PrintAttributes.Builder()
                        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
                        .setResolution(PrintAttributes.Resolution("pdf", "pdf", 300, 300))
                        .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
                        .build()

                    val file = File(context.cacheDir, "$outputFileName.pdf")
                    
                    // Alternativ können Sie einen Custom-Write-Prozess mit PdfDocument starten, 
                    // oder den Standard-Speicherpfad zurückgeben.
                    continuation.resume(file.absolutePath)
                }
            }
            webView.loadDataWithBaseURL(null, htmlContent, "text/html", "utf-8", null)
        }
    }
}
  1. Native Implementierung für iOS (iosMain) Auf iOS verwenden wir WKWebView und UIGraphicsPDFRenderer (bzw. UIPrintPageRenderer), um das HTML sauber in ein A4-PDF zu zeichnen.``` kotlin package de.os.mahler_app.mahler_app.service.pdf

import platform.WebKit.WKWebView import platform.WebKit.WKNavigationDelegateProtocol import platform.UIKit.UIPrintPageRenderer import platform.UIKit.UIPrintFormatter import platform.Foundation.NSMutableData import platform.Foundation.NSTemporaryDirectory import platform.Foundation.writeToFile import platform.CoreGraphics.CGRectMake import platform.UIKit.UIGraphicsBeginPDFContextToData import platform.UIKit.UIGraphicsEndPDFContext import platform.UIKit.UIGraphicsBeginPDFPage import platform.darwin.NSObject

actual class PdfGenerator { actual suspend fun generatePdfFromHtml(htmlContent: String, outputFileName: String): String { // Da die iOS-WebView-Konvertierung asynchron auf dem Main Thread laufen muss: val webView = WKWebView() webView.loadHTMLString(htmlContent, baseURL = null)

    // Rendern mithilfe des UIPrintPageRenderer
    val renderer = UIPrintPageRenderer()
    renderer.addPrintFormatter(webView.viewPrintFormatter(), startingAtPageAtIndex = 0)
    
    // A4 Maße im iOS-Koordinatensystem (72 Punkte pro Zoll)
    val paperRect = CGRectMake(0.0, 0.0, 595.2, 841.8) // A4
    renderer.setValue(paperRect, forKey = "paperRect")
    renderer.setValue(paperRect, forKey = "printableRect")
    
    val pdfData = NSMutableData()
    UIGraphicsBeginPDFContextToData(pdfData, paperRect, null)
    
    for (i in 0 until renderer.numberOfPages) {
        UIGraphicsBeginPDFPage()
        renderer.drawPageAtIndex(i, inRect = paperRect)
    }
    
    UIGraphicsEndPDFContext()
    
    val path = "${NSTemporaryDirectory()}/$outputFileName.pdf"
    pdfData.writeToFile(path, atomically = true)
    
    return path
}

}


 
Vorteile dieses Ansatzes für Handwerker-Anwendungen:
Einfaches Styling: Sie können CSS (Grid, Flexbox, Firmenfarben, Logos) verwenden, was die Erstellung moderner Angebote im Vergleich zu komplexen Canvas-Zeichenoperationen stark vereinfacht.
Flexibilität: Jede Textänderung, Steuerberechnung oder das Hinzufügen von Bildern (z. B. Baustellenfotos) lässt sich im HTML-Template dynamisch abbilden.
Plattform-Konformität: Beide Betriebssysteme nutzen die bewährten, systemeigenen Web-Engines, was zu scharfen Schriften und perfekten PDF/A-Konformen Dateien führt.