preparando el imprimir

This commit is contained in:
2025-10-12 21:42:04 +02:00
parent 6641c1f077
commit 26c2ca543a
41 changed files with 1325 additions and 208 deletions

View File

@ -0,0 +1,11 @@
package com.imprimelibros.erp.pdf;
import java.util.Locale;
import java.util.Map;
public record DocumentSpec(
DocumentType type,
String templateId, // p.ej. "presupuesto-a4"
Locale locale,
Map<String, Object> model // data del documento
) {}

View File

@ -0,0 +1,5 @@
package com.imprimelibros.erp.pdf;
public enum DocumentType {
PRESUPUESTO, PEDIDO, FACTURA
}

View File

@ -0,0 +1,32 @@
package com.imprimelibros.erp.pdf;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Locale;
import java.util.Map;
@RestController
@RequestMapping("/api/pdf")
public class PdfController {
private final PdfService pdfService;
public PdfController(PdfService pdfService) { this.pdfService = pdfService; }
@PostMapping("/{type}/{templateId}")
public ResponseEntity<byte[]> generate(
@PathVariable("type") DocumentType type,
@PathVariable String templateId,
@RequestBody Map<String,Object> model,
Locale locale) {
var spec = new DocumentSpec(type, templateId, locale, model);
var pdf = pdfService.generate(spec);
var fileName = type.name().toLowerCase() + "-" + templateId + ".pdf";
return ResponseEntity.ok()
.header("Content-Type", "application/pdf")
.header("Content-Disposition", "inline; filename=\"" + fileName + "\"")
.body(pdf);
}
}

View File

@ -0,0 +1,18 @@
package com.imprimelibros.erp.pdf;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "imprimelibros.pdf")
public class PdfModuleConfig {
/**
* Mapa: "TYPE:templateId" -> "ruta thymeleaf" (sin extensión).
* Ej: "PRESUPUESTO:presupuesto-a4" -> "pdf/presupuesto-a4"
*/
private Map<String, String> templates;
public Map<String, String> getTemplates() { return templates; }
public void setTemplates(Map<String, String> templates) { this.templates = templates; }
}

View File

@ -0,0 +1,44 @@
package com.imprimelibros.erp.pdf;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.springframework.core.io.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.ByteArrayOutputStream;
@Service
public class PdfRenderer {
@Value("classpath:/static/")
private Resource staticRoot;
public byte[] renderHtmlToPdf(String html) {
try (var baos = new ByteArrayOutputStream()) {
var builder = new PdfRendererBuilder();
builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-Regular.ttf"), "Open Sans",
400, com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle.NORMAL, true);
builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-SemiBold.ttf"), "Open Sans",
600, com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle.NORMAL, true);
builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-Bold.ttf"), "Open Sans", 700,
com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle.NORMAL, true);
builder.useFastMode();
builder.withHtmlContent(html, baseUrl());
builder.toStream(baos);
builder.run();
return baos.toByteArray();
} catch (Exception e) {
throw new IllegalStateException("Error generando PDF", e);
}
}
private String baseUrl() {
try {
return staticRoot.getURL().toString();
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,25 @@
package com.imprimelibros.erp.pdf;
import org.springframework.stereotype.Service;
@Service
public class PdfService {
private final TemplateRegistry registry;
private final PdfTemplateEngine engine;
private final PdfRenderer renderer;
public PdfService(TemplateRegistry registry, PdfTemplateEngine engine, PdfRenderer renderer) {
this.registry = registry;
this.engine = engine;
this.renderer = renderer;
}
public byte[] generate(DocumentSpec spec) {
var template = registry.resolve(spec.type(), spec.templateId());
if (template == null) {
throw new IllegalArgumentException("Plantilla no registrada: " + spec.type() + ":" + spec.templateId());
}
var html = engine.render(template, spec.locale(), spec.model());
return renderer.renderHtmlToPdf(html);
}
}

View File

@ -0,0 +1,22 @@
package com.imprimelibros.erp.pdf;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.springframework.stereotype.Service;
import java.util.Locale;
import java.util.Map;
@Service
public class PdfTemplateEngine {
private final SpringTemplateEngine thymeleaf;
public PdfTemplateEngine(SpringTemplateEngine thymeleaf) {
this.thymeleaf = thymeleaf;
}
public String render(String templateName, Locale locale, Map<String,Object> model) {
Context ctx = new Context(locale);
if (model != null) model.forEach(ctx::setVariable);
return thymeleaf.process(templateName, ctx);
}
}

View File

@ -0,0 +1,18 @@
// com.imprimelibros.erp.pdf.TemplateRegistry.java
package com.imprimelibros.erp.pdf;
import org.springframework.stereotype.Service;
@Service
public class TemplateRegistry {
private final PdfModuleConfig config;
public TemplateRegistry(PdfModuleConfig config) {
this.config = config;
}
public String resolve(DocumentType type, String templateId) {
if (config.getTemplates() == null) return null;
return config.getTemplates().get(type.name() + ":" + templateId);
}
}