From c15fff73ee46171f45eef94d24dcbd154be8ca60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Jim=C3=A9nez?= Date: Sun, 12 Oct 2025 23:28:33 +0200 Subject: [PATCH] trabajando en el pdf --- .../com/imprimelibros/erp/ErpApplication.java | 2 + .../erp/pdf/PdfModuleConfig.java | 11 +- .../imprimelibros/erp/pdf/PdfRenderer.java | 38 +-- .../erp/pdf/TemplateRegistry.java | 8 +- .../com/imprimelibros/erp/presupuesto.zip | Bin 39150 -> 0 bytes src/main/resources/application.properties | 5 +- .../static/assets/css/presupuestopdf.css | 271 ++++++----------- .../imprimelibros/pdf/presupuesto-a4.html | 281 +++++++----------- 8 files changed, 243 insertions(+), 373 deletions(-) delete mode 100644 src/main/java/com/imprimelibros/erp/presupuesto.zip diff --git a/src/main/java/com/imprimelibros/erp/ErpApplication.java b/src/main/java/com/imprimelibros/erp/ErpApplication.java index 441eb0c..0e589a8 100644 --- a/src/main/java/com/imprimelibros/erp/ErpApplication.java +++ b/src/main/java/com/imprimelibros/erp/ErpApplication.java @@ -2,8 +2,10 @@ package com.imprimelibros.erp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; @SpringBootApplication +@ConfigurationPropertiesScan(basePackages = "com.imprimelibros.erp") public class ErpApplication { public static void main(String[] args) { diff --git a/src/main/java/com/imprimelibros/erp/pdf/PdfModuleConfig.java b/src/main/java/com/imprimelibros/erp/pdf/PdfModuleConfig.java index 244f915..7ae5481 100644 --- a/src/main/java/com/imprimelibros/erp/pdf/PdfModuleConfig.java +++ b/src/main/java/com/imprimelibros/erp/pdf/PdfModuleConfig.java @@ -1,17 +1,14 @@ +// com.imprimelibros.erp.pdf.PdfModuleConfig.java package com.imprimelibros.erp.pdf; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; 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 templates; + private Map templates = new HashMap<>(); public Map getTemplates() { return templates; } public void setTemplates(Map templates) { this.templates = templates; } diff --git a/src/main/java/com/imprimelibros/erp/pdf/PdfRenderer.java b/src/main/java/com/imprimelibros/erp/pdf/PdfRenderer.java index 3502808..79c2db5 100644 --- a/src/main/java/com/imprimelibros/erp/pdf/PdfRenderer.java +++ b/src/main/java/com/imprimelibros/erp/pdf/PdfRenderer.java @@ -1,5 +1,6 @@ package com.imprimelibros.erp.pdf; +import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder; import com.openhtmltopdf.pdfboxout.PdfRendererBuilder; import org.springframework.core.io.Resource; import org.springframework.beans.factory.annotation.Value; @@ -11,34 +12,33 @@ import java.io.ByteArrayOutputStream; public class PdfRenderer { @Value("classpath:/static/") - private Resource staticRoot; + private org.springframework.core.io.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); - + var builder = new com.openhtmltopdf.pdfboxout.PdfRendererBuilder(); builder.useFastMode(); - builder.withHtmlContent(html, baseUrl()); + + // 👇 Base URL para que pueda resolver /assets/css/ y /img/ + builder.withHtmlContent(html, staticRoot.getURL().toString()); // .../target/classes/static/ + + + // (Opcional) Registrar fuentes TTF + builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-Regular.ttf"), + "Open Sans", 400, BaseRendererBuilder.FontStyle.NORMAL, true); + builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-SemiBold.ttf"), + "Open Sans", 600, BaseRendererBuilder.FontStyle.NORMAL, true); + builder.useFont(() -> getClass().getResourceAsStream("/static/assets/fonts/OpenSans-Bold.ttf"), + "Open Sans", 700, BaseRendererBuilder.FontStyle.NORMAL, true); + 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; - } - } } + + diff --git a/src/main/java/com/imprimelibros/erp/pdf/TemplateRegistry.java b/src/main/java/com/imprimelibros/erp/pdf/TemplateRegistry.java index 153d88f..71f19c1 100644 --- a/src/main/java/com/imprimelibros/erp/pdf/TemplateRegistry.java +++ b/src/main/java/com/imprimelibros/erp/pdf/TemplateRegistry.java @@ -1,4 +1,3 @@ -// com.imprimelibros.erp.pdf.TemplateRegistry.java package com.imprimelibros.erp.pdf; import org.springframework.stereotype.Service; @@ -9,10 +8,15 @@ public class TemplateRegistry { public TemplateRegistry(PdfModuleConfig config) { this.config = config; + System.out.println("PDF templates registrados => " + config.getTemplates()); } public String resolve(DocumentType type, String templateId) { + String key = type.name() + ":" + templateId; + String keyAlt = type.name() + "_" + templateId; // compatibilidad con properties if (config.getTemplates() == null) return null; - return config.getTemplates().get(type.name() + ":" + templateId); + String value = config.getTemplates().get(key); + if (value == null) value = config.getTemplates().get(keyAlt); + return value; } } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto.zip b/src/main/java/com/imprimelibros/erp/presupuesto.zip deleted file mode 100644 index f40202eff7cd44b0124a58267bc8efdd8d8b1aa1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39150 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 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a020a4d..66f623c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -89,5 +89,6 @@ spring.jpa.properties.hibernate.jdbc.time_zone=UTC # # PDF Templates # -imprimelibros.pdf.templates.PRESUPUESTO\:presupuesto-a4=imprimelibros/pdf/presupuesto-a4 -imprimelibros.pdf.templates.FACTURA\:factura-a4=imprimelibros/pdf/factura-a4 \ No newline at end of file +# PDF Templates +imprimelibros.pdf.templates.PRESUPUESTO_presupuesto-a4=imprimelibros/pdf/presupuesto-a4 +imprimelibros.pdf.templates.FACTURA_factura-a4=imprimelibros/pdf/factura-a4 diff --git a/src/main/resources/static/assets/css/presupuestopdf.css b/src/main/resources/static/assets/css/presupuestopdf.css index 6ad90ee..1b1063a 100644 --- a/src/main/resources/static/assets/css/presupuestopdf.css +++ b/src/main/resources/static/assets/css/presupuestopdf.css @@ -1,225 +1,144 @@ -/* === Fuentes === - * Para HTML→PDF con OpenHTMLtoPDF, es preferible registrar las TTF en el renderer. - * Aun así, incluimos @font-face por si renderizas en navegador. - * Coloca los TTF en /static/fonts/ y ajusta los nombres si procede. - */ +/* Open Sans (rutas relativas desde css → fonts) */ @font-face { font-family: "Open Sans"; - src: url("/assets/fonts/OpenSans-Regular.ttf") format("truetype"); + src: url("../fonts/OpenSans-Regular.ttf") format("truetype"); font-weight: 400; - font-style: normal; } @font-face { font-family: "Open Sans"; - src: url("/assets/fonts/OpenSans-SemiBold.ttf") format("truetype"); + src: url("../fonts/OpenSans-SemiBold.ttf") format("truetype"); font-weight: 600; - font-style: normal; } @font-face { font-family: "Open Sans"; - src: url("/assets/fonts/OpenSans-Bold.ttf") format("truetype"); + src: url("../fonts/OpenSans-Bold.ttf") format("truetype"); font-weight: 700; - font-style: normal; } :root { - --text: #1f2937; - --muted: #6b7280; - --border: #e5e7eb; - --brand: #0ea5e9; /* azul suave */ - --bg-light: #f8fafc; /* gris muy claro */ + --ink: #1b1e28; + --muted: #5b6472; + --accent: #0ea5e9; /* azul tira a cyan */ + --line: #e6e8ef; + --bg-tag: #f4f7fb; } @page { size: A4; - margin: 18mm 15mm 22mm 15mm; - @bottom-right { - content: "Página " counter(page) " / " counter(pages); - } + margin: 15mm 14mm 18mm 14mm; + @bottom-right { content: "Página " counter(page) " / " counter(pages); } } + html, body { font-family: "Open Sans", Arial, sans-serif; - color: var(--text); + color: var(--ink); font-size: 11pt; line-height: 1.35; } -* { box-sizing: border-box; } -.doc-header { +/* Top band */ +.topbar { display: table; width: 100%; - margin-bottom: 10mm; - border-bottom: 2px solid var(--brand); + border-bottom: 2px solid var(--accent); padding-bottom: 6mm; + margin-bottom: 6mm; } -.brand { - display: table-cell; - vertical-align: top; - width: 60%; -} -.logo { - height: 34px; - margin-bottom: 6px; -} -.brand-meta { margin-top: 4px; } -.company-name { - font-weight: 700; - font-size: 12pt; -} -.company-meta { - color: var(--muted); - font-size: 9.5pt; -} +.brand { display: table-cell; width: 60%; vertical-align: top; } +.logo { height: 30px; display: block; margin-bottom: 4px; } +.brand-name { font-weight: 700; } +.brand-meta { color: var(--muted); font-size: 9.5pt; } +.doc-title { display: table-cell; width: 40%; text-align: right; font-weight: 700; letter-spacing: 3px; } -.doc-title { - display: table-cell; - vertical-align: top; - width: 40%; - text-align: right; -} -.title-main { - font-size: 18pt; - font-weight: 700; - letter-spacing: 0.5px; -} -.doc-info { - margin-top: 6px; - margin-left: auto; - font-size: 10pt; - border-collapse: collapse; -} -.doc-info td { - padding: 3px 0 3px 12px; - border-bottom: 1px solid var(--border); -} -.doc-info td:first-child { - color: var(--muted); - padding-left: 0; -} -.right { text-align: right; } - -.blocks { - display: table; +/* ficha superior */ +.sheet-info { width: 100%; - table-layout: fixed; - margin-bottom: 8mm; + border-collapse: collapse; + margin: 4mm 0 6mm 0; + font-size: 10.5pt; } -.block { - display: table-cell; - width: 50%; - padding-right: 6mm; - vertical-align: top; +.sheet-info td { + border: 1px solid var(--line); + padding: 4px 6px; } -.block:last-child { padding-right: 0; } +.sheet-info .lbl { color: var(--muted); margin-right: 4px; } +.sheet-info .val { font-weight: 700; } + +/* Línea título libro */ +.line-title { + margin: 3mm 0 5mm 0; + padding: 4px 6px; + background: var(--bg-tag); + border-left: 3px solid var(--accent); + font-size: 10.5pt; +} +.line-title .lbl { color: var(--muted); margin-right: 6px; font-weight: 600; } + +/* Specs 2 columnas */ +.specs { display: table; width: 100%; table-layout: fixed; margin-bottom: 6mm; } +.specs .col { display: table-cell; width: 50%; padding-right: 6mm; vertical-align: top; } +.specs .col:last-child { padding-right: 0; } .block-title { - font-weight: 600; text-transform: uppercase; + font-weight: 700; + color: var(--accent); font-size: 10pt; - color: var(--brand); - margin-bottom: 2mm; -} -.block-body { - border: 1px solid var(--border); - padding: 4mm; - background: var(--bg-light); -} -.row { - margin-bottom: 2mm; - display: block; -} -.row .label { - display: inline-block; - width: 27%; - color: var(--muted); -} -.row .value { - display: inline-block; - width: 70%; - vertical-align: top; + margin: 2mm 0 1mm 0; } +.kv { margin: 1mm 0; } +.kv span { color: var(--muted); display: inline-block; min-width: 55%; } +.kv b { font-weight: 600; } +.subblock { margin-top: 3mm; } -.table-section .section-title, -.notes .section-title { - font-weight: 600; - font-size: 10.5pt; - margin: 6mm 0 2mm 0; -} +.services { margin: 0; padding-left: 14px; } +.services li { margin: 1mm 0; } -.items { - width: 100%; - border-collapse: collapse; - font-size: 10.5pt; -} -.items thead th { - text-align: left; - border-bottom: 2px solid var(--brand); - padding: 6px 6px; - background: #eef8fe; - font-weight: 600; -} -.items tbody td { - border-bottom: 1px solid var(--border); - padding: 6px 6px; - vertical-align: top; -} -.items .col-right { text-align: right; } -.items .col-center { text-align: center; } -.items .col-desc .desc { font-weight: 600; } -.items .col-desc .meta { color: var(--muted); font-size: 9.5pt; margin-top: 2px; } -.items .group-header td { - background: var(--bg-light); - color: var(--brand); - font-weight: 600; -} - -.totals { - margin-top: 6mm; - display: block; - width: 100%; -} -.totals-table { - margin-left: auto; - border-collapse: collapse; - min-width: 70mm; -} -.totals-table td { - padding: 4px 0 4px 14px; - border-bottom: 1px solid var(--border); -} -.totals-table td:first-child { - color: var(--muted); - padding-left: 0; -} -.totals-table .total-row td { - border-bottom: 2px solid var(--brand); - padding-top: 6px; - padding-bottom: 6px; -} -.totals-table .right { text-align: right; } - -.notes .note-text { - border: 1px solid var(--border); - padding: 4mm; +/* Bloque marcapáginas */ +.bookmark { + margin-top: 4mm; + border: 1px dashed var(--line); + padding: 3mm; background: #fff; - font-size: 10pt; - white-space: pre-wrap; +} +.bookmark .bk-title { + font-weight: 700; margin-bottom: 2mm; } -.doc-footer { +/* Tabla de precios (tiradas) */ +.prices { + width: 100%; + border-collapse: collapse; + margin-top: 6mm; + font-size: 10.5pt; +} +.prices thead th { + text-align: left; + padding: 6px; + border-bottom: 2px solid var(--accent); + background: #eef8fe; + font-weight: 700; +} +.prices tbody td { + border-bottom: 1px solid var(--line); + padding: 6px; +} +.prices .col-tirada { width: 22%; font-weight: 700; } + +/* Footer */ +.footer { margin-top: 8mm; + border-top: 1px solid var(--line); padding-top: 4mm; - border-top: 1px solid var(--border); display: table; width: 100%; + font-size: 9.5pt; color: var(--muted); - font-size: 9pt; -} -.footer-left { display: table-cell; } -.footer-right { - display: table-cell; - text-align: right; } +.footer .address { display: table-cell; width: 45%; } +.footer .privacy { display: table-cell; width: 55%; } +.pv-title { font-weight: 700; margin-bottom: 1mm; color: var(--ink); } +.pv-text { line-height: 1.25; } -/* Compatibilidad PDF (OpenHTMLtoPDF) */ -.page-number::after { content: counter(page); } -.page-count::after { content: counter(pages); } +.page-count { margin-top: 2mm; text-align: right; font-size: 9pt; color: var(--muted); } +.page::after { content: counter(page); } +.pages::after { content: counter(pages); } diff --git a/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html b/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html index 23141d3..6e58aeb 100644 --- a/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html +++ b/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html @@ -3,193 +3,140 @@ Presupuesto - + - -
+ +
- -
-
ImprimeLibros ERP
-
- C/ Dirección 123, 28000 Madrid
- +34 600 000 000 · - info@imprimelibros.com
- B-12345678 + +
+
ImprimeLibros ERP
+
+ C. José Picón, 28 local A · 28028 Madrid + · 910052574 + · info@imprimelibros.com + · B04998886
-
-
PRESUPUESTO
- - - - - - - - - - - - - -
2025-00123
Fecha12/10/2025
Validez30 días
-
-
+
P R E S U P U E S T O
+ - -
-
-
Cliente
-
-
- Nombre: - Editorial Ejemplo S.L. -
-
- CIF/NIF: - B-00000000 -
-
- Dirección: - Av. de los Libros, 45 -
-
- Localidad: - - - - - -
-
- Email: - comercial@editorial.com -
+ + + + + + + +
PRESUPUESTO Nº: 153153CLIENTE: JUAN JOSÉ MÉNDEZFECHA: 10/10/2025
+ + +
+ Título: + Libro de prueba +
+ + +
+
+
Encuadernación
+
Encuadernación:Fresado
+
Formato: + 148x210 mm +
+
Páginas:132
+
Páginas Negro:100
+
Páginas Color:32
+ +
+
Interior
+
Tipo de impresión:Color Premium
+
Papel interior:Estucado Mate
+
Gramaje interior:115
-
-
Proyecto
-
-
- Título: - Libro de Ejemplo +
+
+
Cubierta
+
Tipo de cubierta:Tapa blanda
+
Solapas:
+
Tamaño solapas:80 mm
+
Impresión:Una cara
+
Papel cubierta:Estucado mate
+
Gramaje cubierta:250
+
Acabado:Plastificado Brillo 1/C
+
+ +
+
Servicios Extras
+ +
+
    +
  • + Ferro Digital + 0,00 € +
  • +
-
- Autor: - Autor/a -
-
- ISBN: - 978-1-2345-6789-0 -
-
- Formato: - - 150 × 210 mm - (personalizado) - -
-
- Páginas: - - - - -
-
- Tiradas: - 300, 500, 1000 + + +
+
Marcapáginas
+
Tamaño:50x210
+
Papel:Estucado mate 300 g
+
Impresión:Una cara
+
Plastificado:Brillo 1/C
-
+ - -
-
Detalle del presupuesto
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DescripciónUdsPrecio unit.Dto.Importe
-
Impresión interior B/N 80 g
-
- 300 páginas · offset 80 g · tinta negra -
-
10002,1500-2.150,00
Servicios adicionales
-
Transporte
-
190,00-90,00
-
- - -
- + +
+ - - + + + + + - - - + + + + + + + + + - - - - - - - - -
Base imponible2.180,00TIRADAIMPRESIÓNIVATOTALUNIDAD
IVA (21%)457,80
100 uds.152,15€7,68€159,99€1,52€
Total2.637,80
Peso estimado120,00 kg
-
+ + - -
-
Observaciones
-
Presupuesto válido 30 días.
- -
Condiciones
-
- Entrega estimada 7-10 días laborables tras confirmación de artes finales. + +
+
+
Política de privacidad
+
+ Responsable: Impresión Imprime Libros - + CIF: B04998886 - + Email: info@imprimelibros.com · + Dirección: Calle José Picón, Nº 28 Local A, 28028, Madrid. + Sus datos se tratarán para la adecuada gestión fiscal y contable… +
+
+ - -
- - -
+
Página /