Ajout du support pour la génération de PDF de kit de retouche.
This commit is contained in:
parent
4283f9756c
commit
ced46dd83d
|
@ -1,42 +0,0 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service.files;
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.utils.PdfBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Service
|
||||
public class TouchUpKitService {
|
||||
|
||||
private static final String TOUCH_UP_FR = "KIT DE RETOUCHE";
|
||||
private static final String TOUCH_UP_EN = "TOUCH UP KIT";
|
||||
public static final int FONT_SIZE = 42;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
@Autowired
|
||||
public TouchUpKitService(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un PDF de kit de retouche pour une job.
|
||||
*
|
||||
* @param jobNumber La job
|
||||
* @return Le PDF de kit de retouche pour la job
|
||||
*/
|
||||
public byte[] generatePdfForJobNumber(String jobNumber) {
|
||||
try {
|
||||
return new PdfBuilder(resourceLoader, true, FONT_SIZE)
|
||||
.addLine(TOUCH_UP_FR, true, 0)
|
||||
.addLine(TOUCH_UP_EN, true, 0)
|
||||
.addLine(jobNumber, false, 10)
|
||||
.build();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(String.format("Impossible de générer un PDF de kit de retouche pour la job '%s': %s", jobNumber, ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package dev.fyloz.colorrecipesexplorer.utils;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PdfBuilder {
|
||||
|
||||
private static final String PATH_FONT_ARIAL_BOLD = "classpath:fonts/arialbd.ttf";
|
||||
|
||||
private final PDFont font;
|
||||
private final PDDocument document = new PDDocument();
|
||||
private final PDPage page = new PDPage();
|
||||
private final Collection<PdfLine> lines = new ArrayList<>();
|
||||
private final boolean duplicated;
|
||||
private final int fontSize;
|
||||
private final int fontSizeBold;
|
||||
private final int lineSpacing;
|
||||
|
||||
public PdfBuilder(ResourceLoader resourceLoader, boolean duplicated, int fontSize) throws IOException {
|
||||
this.duplicated = duplicated;
|
||||
this.fontSize = fontSize;
|
||||
this.fontSizeBold = this.fontSize + 12;
|
||||
this.lineSpacing = (int) (this.fontSize * 1.5f);
|
||||
|
||||
document.addPage(page);
|
||||
font = PDType0Font.load(document, resourceLoader.getResource(PATH_FONT_ARIAL_BOLD).getInputStream());
|
||||
}
|
||||
|
||||
public PdfBuilder addLine(String text, boolean bold, int marginTop) {
|
||||
lines.add(new PdfLine(text, bold, marginTop));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] build() throws IOException {
|
||||
writeContent();
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
document.save(outputStream);
|
||||
document.close();
|
||||
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
private void writeContent() throws IOException {
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, page);
|
||||
contentStream.beginText();
|
||||
|
||||
int marginTop = 30;
|
||||
for (PdfLine line : lines) {
|
||||
writeCenteredText(contentStream, line, marginTop);
|
||||
marginTop += lineSpacing;
|
||||
}
|
||||
|
||||
if (duplicated) {
|
||||
marginTop = (int) page.getMediaBox().getHeight() / 2;
|
||||
for (PdfLine line : lines) {
|
||||
writeCenteredText(contentStream, line, marginTop);
|
||||
marginTop += lineSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.endText();
|
||||
contentStream.close();
|
||||
}
|
||||
|
||||
private void writeCenteredText(PDPageContentStream contentStream, PdfLine line, int marginTop) throws IOException {
|
||||
float textWidth = font.getStringWidth(line.getText()) / 1000 * (line.isBold() ? fontSizeBold : fontSize);
|
||||
float textHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * (line.isBold() ? fontSizeBold : fontSize);
|
||||
float textX = (page.getMediaBox().getWidth() - textWidth) / 2f;
|
||||
float textY = (page.getMediaBox().getHeight() - (marginTop + line.getMarginTop()) - textHeight);
|
||||
|
||||
if (line.isBold()) contentStream.setFont(font, fontSizeBold);
|
||||
else contentStream.setFont(font, fontSize);
|
||||
|
||||
contentStream.newLineAtOffset(textX, textY);
|
||||
contentStream.showText(line.getText());
|
||||
contentStream.newLineAtOffset(-textX, -textY); // Réinitialise la position pour la prochaine ligne
|
||||
}
|
||||
|
||||
public static class PdfLine {
|
||||
|
||||
private String text;
|
||||
private boolean bold;
|
||||
private int marginTop;
|
||||
|
||||
public PdfLine() {
|
||||
}
|
||||
|
||||
public PdfLine(String text, boolean bold, int marginTop) {
|
||||
this.text = text;
|
||||
this.bold = bold;
|
||||
this.marginTop = marginTop;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public boolean isBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
public void setBold(boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public int getMarginTop() {
|
||||
return marginTop;
|
||||
}
|
||||
|
||||
public void setMarginTop(int marginTop) {
|
||||
this.marginTop = marginTop;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,4 +6,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties
|
|||
class CreProperties {
|
||||
var workingDirectory: String = "data"
|
||||
var deploymentUrl: String = "http://localhost"
|
||||
var cacheGeneratedFiles: Boolean = false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package dev.fyloz.colorrecipesexplorer.rest.files
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.service.files.TouchUpKitService
|
||||
import org.springframework.core.io.ByteArrayResource
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/touchup")
|
||||
class TouchUpKitController(
|
||||
private val touchUpKitService: TouchUpKitService
|
||||
) {
|
||||
@GetMapping
|
||||
fun getJobPdf(@RequestParam job: String): ResponseEntity<ByteArrayResource> {
|
||||
with(touchUpKitService.generateJobPdfResource(job)) {
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Disposition", "filename=TouchUpKit_$job.pdf")
|
||||
.contentLength(this.contentLength())
|
||||
.contentType(MediaType.APPLICATION_PDF)
|
||||
.body(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package dev.fyloz.colorrecipesexplorer.service
|
|||
|
||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.mapMayThrow
|
||||
import dev.fyloz.colorrecipesexplorer.utils.mapMayThrow
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
|
|
@ -3,8 +3,8 @@ package dev.fyloz.colorrecipesexplorer.service
|
|||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MixMaterialRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.findDuplicated
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.hasGaps
|
||||
import dev.fyloz.colorrecipesexplorer.utils.findDuplicated
|
||||
import dev.fyloz.colorrecipesexplorer.utils.hasGaps
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
|
|
|
@ -2,7 +2,7 @@ package dev.fyloz.colorrecipesexplorer.service
|
|||
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MixRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.setAll
|
||||
import dev.fyloz.colorrecipesexplorer.utils.setAll
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
|
|
@ -4,7 +4,7 @@ import dev.fyloz.colorrecipesexplorer.model.*
|
|||
import dev.fyloz.colorrecipesexplorer.model.validation.or
|
||||
import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.files.FileService
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.setAll
|
||||
import dev.fyloz.colorrecipesexplorer.utils.setAll
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
|
@ -222,9 +222,9 @@ class RecipeImageServiceImpl(
|
|||
this@getDirectory.imagesDirectoryPath.fullPath().path
|
||||
})
|
||||
|
||||
fun getImageFileName(recipe: Recipe, id: Long) =
|
||||
private fun getImageFileName(recipe: Recipe, id: Long) =
|
||||
"${recipe.name}$RECIPE_IMAGE_ID_DELIMITER$id"
|
||||
|
||||
fun getImagePath(recipe: Recipe, name: String) =
|
||||
private fun getImagePath(recipe: Recipe, name: String) =
|
||||
"${recipe.imagesDirectoryPath}/$name$RECIPE_IMAGE_EXTENSION"
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package dev.fyloz.colorrecipesexplorer.service
|
|||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.RecipeStepRepository
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.findDuplicated
|
||||
import dev.fyloz.colorrecipesexplorer.service.utils.hasGaps
|
||||
import dev.fyloz.colorrecipesexplorer.utils.findDuplicated
|
||||
import dev.fyloz.colorrecipesexplorer.utils.hasGaps
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
|
|
|
@ -28,9 +28,12 @@ interface FileService {
|
|||
/** Creates a file at the given [path]. */
|
||||
fun create(path: String)
|
||||
|
||||
/** Writes the given [file] at the given [path]. If the file already exists, it will be overwritten if [overwrite] is true. */
|
||||
/** Writes the given [file] to the given [path]. If the file already exists, it will be overwritten if [overwrite] is enabled. */
|
||||
fun write(file: MultipartFile, path: String, overwrite: Boolean)
|
||||
|
||||
/** Writes the given [data] to the given [path]. If the file at the path already exists, it will be overwritten if [overwrite] is enabled. */
|
||||
fun write(data: ByteArrayResource, path: String, overwrite: Boolean)
|
||||
|
||||
/** Deletes the file at the given [path]. */
|
||||
fun delete(path: String)
|
||||
|
||||
|
@ -71,23 +74,15 @@ class FileServiceImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun write(file: MultipartFile, path: String, overwrite: Boolean) {
|
||||
val fullPath = path.fullPath()
|
||||
|
||||
if (exists(path)) {
|
||||
if (!overwrite) throw FileExistsException(path)
|
||||
} else {
|
||||
create(path)
|
||||
override fun write(file: MultipartFile, path: String, overwrite: Boolean) =
|
||||
prepareWrite(path, overwrite) {
|
||||
file.transferTo(this.toPath())
|
||||
}
|
||||
|
||||
try {
|
||||
withFileAt(fullPath) {
|
||||
file.transferTo(this.toPath())
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
FileWriteException(path).logAndThrow(ex, logger)
|
||||
override fun write(data: ByteArrayResource, path: String, overwrite: Boolean) =
|
||||
prepareWrite(path, overwrite) {
|
||||
this.writeBytes(data.byteArray)
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(path: String) {
|
||||
try {
|
||||
|
@ -108,6 +103,24 @@ class FileServiceImpl(
|
|||
return FilePath("${creProperties.workingDirectory}/$this")
|
||||
}
|
||||
|
||||
private fun prepareWrite(path: String, overwrite: Boolean, op: File.() -> Unit) {
|
||||
val fullPath = path.fullPath()
|
||||
|
||||
if (exists(path)) {
|
||||
if (!overwrite) throw FileExistsException(path)
|
||||
} else {
|
||||
create(path)
|
||||
}
|
||||
|
||||
try {
|
||||
withFileAt(fullPath) {
|
||||
this.op()
|
||||
}
|
||||
} catch (ex: IOException) {
|
||||
FileWriteException(path).logAndThrow(ex, logger)
|
||||
}
|
||||
}
|
||||
|
||||
/** Runs the given [block] in the context of a file with the given [fullPath]. */
|
||||
private fun <T> withFileAt(fullPath: FilePath, block: File.() -> T) =
|
||||
fullPath.file.block()
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service.files
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||
import dev.fyloz.colorrecipesexplorer.utils.*
|
||||
import org.springframework.core.io.ByteArrayResource
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
private const val TOUCH_UP_KIT_FILES_PATH = "pdf/touchupkits"
|
||||
|
||||
const val TOUCH_UP_TEXT_FR = "KIT DE RETOUCHE"
|
||||
const val TOUCH_UP_TEXT_EN = "TOUCH UP KIT"
|
||||
|
||||
interface TouchUpKitService {
|
||||
/** Generates and returns a [PdfDocument] for the given [job]. */
|
||||
fun generateJobPdf(job: String): PdfDocument
|
||||
|
||||
/**
|
||||
* Generates and returns a [PdfDocument] for the given [job] as a [ByteArrayResource].
|
||||
*
|
||||
* If [CreProperties.cacheGeneratedFiles] is enabled and a file exists for the job, its content will be returned.
|
||||
* If caching is enabled but no file exists for the job, the generated ByteArrayResource will be cached on the disk.
|
||||
*/
|
||||
fun generateJobPdfResource(job: String): ByteArrayResource
|
||||
|
||||
/** Writes the given [document] to the [FileService] if [CreProperties.cacheGeneratedFiles] is enabled. */
|
||||
fun String.cachePdfDocument(document: PdfDocument)
|
||||
}
|
||||
|
||||
@Service
|
||||
class TouchUpKitServiceImpl(
|
||||
private val fileService: FileService,
|
||||
private val creProperties: CreProperties
|
||||
) : TouchUpKitService {
|
||||
override fun generateJobPdf(job: String) = pdf {
|
||||
container {
|
||||
centeredVertically = true
|
||||
drawContainerBottom = true
|
||||
text(TOUCH_UP_TEXT_FR) {
|
||||
bold = true
|
||||
fontSize = PDF_DEFAULT_FONT_SIZE + 12
|
||||
}
|
||||
text(TOUCH_UP_TEXT_EN) {
|
||||
bold = true
|
||||
fontSize = PDF_DEFAULT_FONT_SIZE + 12
|
||||
}
|
||||
text(job) {
|
||||
marginTop = 10f
|
||||
}
|
||||
}
|
||||
|
||||
container(containers[0]) {
|
||||
drawContainerBottom = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateJobPdfResource(job: String): ByteArrayResource {
|
||||
if (creProperties.cacheGeneratedFiles) {
|
||||
with(job.pdfDocumentPath()) {
|
||||
if (fileService.exists(this)) {
|
||||
return fileService.read(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return generateJobPdf(job).apply {
|
||||
job.cachePdfDocument(this)
|
||||
}.toByteArrayResource()
|
||||
}
|
||||
|
||||
override fun String.cachePdfDocument(document: PdfDocument) {
|
||||
if (!creProperties.cacheGeneratedFiles) return
|
||||
|
||||
fileService.write(document.toByteArrayResource(), this.pdfDocumentPath(), true)
|
||||
}
|
||||
|
||||
private fun String.pdfDocumentPath() =
|
||||
"$TOUCH_UP_KIT_FILES_PATH/$this.pdf"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service.utils
|
||||
package dev.fyloz.colorrecipesexplorer.utils
|
||||
|
||||
/** Returns a list containing the result of the given [transform] applied to each item of the [Iterable]. If the given [transform] throws, the [Throwable] will be passed to the given [throwableConsumer]. */
|
||||
inline fun <T, R, reified E : Throwable> Iterable<T>.mapMayThrow(
|
|
@ -0,0 +1,125 @@
|
|||
package dev.fyloz.colorrecipesexplorer.utils
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument
|
||||
import org.apache.pdfbox.pdmodel.PDPage
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font
|
||||
import org.springframework.core.io.ByteArrayResource
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
val PDF_DEFAULT_FONT: PDType1Font = PDType1Font.HELVETICA
|
||||
val PDF_DEFAULT_FONT_BOLD: PDType1Font = PDType1Font.HELVETICA_BOLD
|
||||
const val PDF_DEFAULT_FONT_SIZE = 42f
|
||||
val PDF_DASH_LINE_PATTERN = floatArrayOf(4f)
|
||||
|
||||
/** Creates a [PdfContainer] and apply the given [block]. */
|
||||
fun pdf(block: PdfDocument.() -> Unit = {}) =
|
||||
PdfDocument().apply { block() }
|
||||
|
||||
/** Creates a [PdfContainer] in the given [PdfDocument] and apply the given [block]. If a [container] is given, the receiver of the block will be a clone of it. */
|
||||
fun PdfDocument.container(container: PdfContainer = PdfContainer(), block: PdfContainer.() -> Unit) {
|
||||
this.containers += PdfContainer(container).apply(block)
|
||||
}
|
||||
|
||||
/** Creates a [PdfText] with the given [text] in the given [PdfContainer] and apply the given [block]. */
|
||||
fun PdfContainer.text(text: String, block: PdfText.() -> Unit) {
|
||||
this.texts += PdfText(text = text).apply(block)
|
||||
}
|
||||
|
||||
fun PdfDocument.toByteArrayResource(): ByteArrayResource = PDDocument().use { document ->
|
||||
val page = PDPage()
|
||||
|
||||
document.addPage(page)
|
||||
|
||||
fun PDPageContentStream.drawText(text: PdfText, y: Float) {
|
||||
val font = if (text.bold) fontBold else font
|
||||
val textWidth = font.getStringWidth(text.text) / 1000 * text.fontSize
|
||||
val textX = (page.mediaBox.width - textWidth) / 2f
|
||||
|
||||
beginText()
|
||||
newLineAtOffset(textX, y)
|
||||
setFont(font, text.fontSize)
|
||||
showText(text.text)
|
||||
endText()
|
||||
}
|
||||
|
||||
fun PDPageContentStream.drawDashLine(y: Float) {
|
||||
moveTo(0f, y)
|
||||
lineTo(page.mediaBox.width, y)
|
||||
setLineDashPattern(PDF_DASH_LINE_PATTERN, 0f)
|
||||
stroke()
|
||||
}
|
||||
|
||||
fun PDPageContentStream.drawContainer(container: PdfContainer, y: Float, height: Float) {
|
||||
var textY = y
|
||||
|
||||
if (container.centeredVertically) {
|
||||
val textsHeight = container.texts
|
||||
.map { it.fontSize + it.marginTop }
|
||||
.reduce { acc, textHeight -> acc + textHeight }
|
||||
textY -= (height - textsHeight) / 2f
|
||||
}
|
||||
|
||||
if (container.drawContainerBottom) {
|
||||
this.drawDashLine(y - height)
|
||||
}
|
||||
|
||||
container.texts.forEach { text ->
|
||||
textY -= text.fontSize + text.marginTop
|
||||
this.drawText(text, textY)
|
||||
}
|
||||
}
|
||||
|
||||
PDPageContentStream(document, page).use {
|
||||
var containerY = page.mediaBox.height
|
||||
|
||||
val computedSizeContainerCount = containers
|
||||
.filter { it.height < 0 }
|
||||
.count()
|
||||
val computedSizeContainersHeight = containerY / computedSizeContainerCount
|
||||
|
||||
containers.forEach { container ->
|
||||
val height = if (container.height < 0)
|
||||
computedSizeContainersHeight
|
||||
else
|
||||
container.height
|
||||
|
||||
it.drawContainer(container, containerY, height)
|
||||
|
||||
containerY -= height
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream().use {
|
||||
document.save(it)
|
||||
ByteArrayResource(it.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
data class PdfDocument(
|
||||
var font: PDFont = PDF_DEFAULT_FONT,
|
||||
var fontBold: PDFont = PDF_DEFAULT_FONT_BOLD,
|
||||
val containers: MutableList<PdfContainer> = mutableListOf()
|
||||
)
|
||||
|
||||
data class PdfContainer(
|
||||
var height: Float = -1f,
|
||||
var centeredVertically: Boolean = false,
|
||||
var drawContainerBottom: Boolean = false,
|
||||
val texts: MutableList<PdfText> = mutableListOf()
|
||||
) {
|
||||
constructor(container: PdfContainer) : this(
|
||||
container.height,
|
||||
container.centeredVertically,
|
||||
container.drawContainerBottom,
|
||||
container.texts
|
||||
)
|
||||
}
|
||||
|
||||
data class PdfText(
|
||||
var text: String = "Text",
|
||||
var bold: Boolean = false,
|
||||
var fontSize: Float = PDF_DEFAULT_FONT_SIZE,
|
||||
var marginTop: Float = 0f
|
||||
)
|
|
@ -3,6 +3,7 @@ server.port=9090
|
|||
# CRE
|
||||
cre.server.working-directory=data
|
||||
cre.server.deployment-url=http://localhost:9090
|
||||
cre.server.cache-generated-files=true
|
||||
cre.security.jwt-secret=CtnvGQjgZ44A1fh295gE
|
||||
cre.security.jwt-duration=18000000
|
||||
# Root user
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.mockk.*
|
|||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.mock.web.MockMultipartFile
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
|
@ -165,7 +166,7 @@ class RecipeServiceTest :
|
|||
|
||||
private class RecipeImageServiceTestContext {
|
||||
val fileService = mockk<FileService> {
|
||||
every { write(any(), any(), any()) } just Runs
|
||||
every { write(any<MultipartFile>(), any(), any()) } just Runs
|
||||
every { delete(any()) } just Runs
|
||||
}
|
||||
val recipeImageService = spyk(RecipeImageServiceImpl(fileService))
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package dev.fyloz.colorrecipesexplorer.service.files
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||
import dev.fyloz.colorrecipesexplorer.utils.PdfDocument
|
||||
import dev.fyloz.colorrecipesexplorer.utils.toByteArrayResource
|
||||
import io.mockk.*
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.core.io.ByteArrayResource
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
private class TouchUpKitServiceTestContext {
|
||||
val fileService = mockk<FileService> {
|
||||
every { write(any<ByteArrayResource>(), any(), any()) } just Runs
|
||||
}
|
||||
val creProperties = mockk<CreProperties> {
|
||||
every { cacheGeneratedFiles } returns false
|
||||
}
|
||||
val touchUpKitService = spyk(TouchUpKitServiceImpl(fileService, creProperties))
|
||||
val pdfDocumentData = mockk<ByteArrayResource>()
|
||||
val pdfDocument = mockk<PdfDocument> {
|
||||
mockkStatic(PdfDocument::toByteArrayResource)
|
||||
every { toByteArrayResource() } returns pdfDocumentData
|
||||
}
|
||||
}
|
||||
|
||||
class TouchUpKitServiceTest {
|
||||
private val job = "job"
|
||||
|
||||
@AfterEach
|
||||
internal fun afterEach() {
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
// generateJobPdf()
|
||||
|
||||
@Test
|
||||
fun `generateJobPdf() generates a valid PdfDocument for the given job`() {
|
||||
test {
|
||||
val generatedPdfDocument = touchUpKitService.generateJobPdf(job)
|
||||
|
||||
setOf(0, 1).forEach {
|
||||
assertEquals(TOUCH_UP_TEXT_FR, generatedPdfDocument.containers[it].texts[0].text)
|
||||
assertEquals(TOUCH_UP_TEXT_EN, generatedPdfDocument.containers[it].texts[1].text)
|
||||
assertEquals(job, generatedPdfDocument.containers[it].texts[2].text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateJobPdfResource()
|
||||
|
||||
@Test
|
||||
fun `generateJobPdfResource() generates and returns a ByteArrayResource for the given job then cache it`() {
|
||||
test {
|
||||
every { touchUpKitService.generateJobPdf(any()) } returns pdfDocument
|
||||
with(touchUpKitService) {
|
||||
every { job.cachePdfDocument(pdfDocument) } just Runs
|
||||
}
|
||||
|
||||
val generatedResource = touchUpKitService.generateJobPdfResource(job)
|
||||
|
||||
assertEquals(pdfDocumentData, generatedResource)
|
||||
|
||||
verify {
|
||||
with(touchUpKitService) {
|
||||
job.cachePdfDocument(pdfDocument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateJobPdfResource() returns a cached ByteArrayResource from the FileService when caching is enabled and a cached file eixsts for the given job`() {
|
||||
test {
|
||||
every { creProperties.cacheGeneratedFiles } returns true
|
||||
every { fileService.exists(any()) } returns true
|
||||
every { fileService.read(any()) } returns pdfDocumentData
|
||||
|
||||
val redResource = touchUpKitService.generateJobPdfResource(job)
|
||||
|
||||
assertEquals(pdfDocumentData, redResource)
|
||||
}
|
||||
}
|
||||
|
||||
// String.cachePdfDocument()
|
||||
|
||||
@Test
|
||||
fun `cachePdfDocument() does nothing when caching is disabled`() {
|
||||
test {
|
||||
every { creProperties.cacheGeneratedFiles } returns false
|
||||
|
||||
with(touchUpKitService) {
|
||||
job.cachePdfDocument(pdfDocument)
|
||||
}
|
||||
|
||||
verify(exactly = 0) {
|
||||
fileService.write(any<ByteArrayResource>(), any(), any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cachePdfDocument() writes the given document to the FileService when cache is enabled`() {
|
||||
test {
|
||||
every { creProperties.cacheGeneratedFiles } returns true
|
||||
|
||||
with(touchUpKitService) {
|
||||
job.cachePdfDocument(pdfDocument)
|
||||
}
|
||||
|
||||
verify {
|
||||
fileService.write(pdfDocumentData, any(), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun test(test: TouchUpKitServiceTestContext.() -> Unit) {
|
||||
TouchUpKitServiceTestContext().test()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue