mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-02-28 13:49:12 +00:00
terminados primeras modificaciones
This commit is contained in:
@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import jakarta.validation.Validator;
|
||||
@ -151,7 +152,8 @@ public class PresupuestoController {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||
Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
|
||||
locale);
|
||||
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||
resultado.put("lomo", datosInterior.get("lomo"));
|
||||
resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale));
|
||||
@ -273,7 +275,8 @@ public class PresupuestoController {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||
Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
|
||||
locale);
|
||||
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||
resultado.put("lomo", datosInterior.get("lomo"));
|
||||
|
||||
@ -309,10 +312,11 @@ public class PresupuestoController {
|
||||
presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción
|
||||
}
|
||||
}
|
||||
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||
Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
|
||||
locale);
|
||||
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||
resultado.put("lomo", datosInterior.get("lomo"));
|
||||
|
||||
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@ -335,10 +339,11 @@ public class PresupuestoController {
|
||||
}
|
||||
|
||||
Map<String, Object> resultado = new HashMap<>();
|
||||
Map<String , Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale);
|
||||
Map<String, Object> datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto),
|
||||
locale);
|
||||
resultado.put("solapas", datosInterior.get("maxSolapas"));
|
||||
resultado.put("lomo", datosInterior.get("lomo"));
|
||||
|
||||
|
||||
return ResponseEntity.ok(resultado);
|
||||
}
|
||||
|
||||
@ -650,8 +655,7 @@ public class PresupuestoController {
|
||||
"presupuesto.reimprimir.success.title",
|
||||
"presupuesto.reimprimir.success.text",
|
||||
"presupuesto.reimprimir.error.title",
|
||||
"presupuesto.reimprimir.error.internal"
|
||||
);
|
||||
"presupuesto.reimprimir.error.internal");
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
@ -676,7 +680,7 @@ public class PresupuestoController {
|
||||
"presupuesto.add.error.save.title",
|
||||
"presupuesto.iva-reducido",
|
||||
"presupuesto.iva-reducido-descripcion",
|
||||
"presupuesto.duplicar.title",
|
||||
"presupuesto.duplicar.title",
|
||||
"presupuesto.duplicar.text",
|
||||
"presupuesto.duplicar.confirm",
|
||||
"presupuesto.duplicar.cancelar",
|
||||
@ -908,11 +912,11 @@ public class PresupuestoController {
|
||||
return ResponseEntity.badRequest().body(errores);
|
||||
}
|
||||
|
||||
try{
|
||||
try {
|
||||
Map<String, Object> lomos = presupuestoService.obtenerLomos(presupuesto);
|
||||
presupuesto.setLomo(Double.valueOf(lomos.get("lomoInterior").toString()));
|
||||
presupuesto.setLomoCubierta(Double.valueOf(lomos.get("lomoCubierta").toString()));
|
||||
}catch(Exception ex){
|
||||
} catch (Exception ex) {
|
||||
log.error("Error al obtener lomos desde SK API", ex);
|
||||
}
|
||||
|
||||
@ -941,6 +945,30 @@ public class PresupuestoController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/api/plantilla-cubierta.png")
|
||||
public ResponseEntity<byte[]> getPlantillaCubierta(
|
||||
@RequestParam("tipo") String tipoLibro,
|
||||
@RequestParam("tapa") String tapa,
|
||||
@RequestParam("ancho") Integer ancho,
|
||||
@RequestParam("alto") Integer alto,
|
||||
@RequestParam("lomo") Integer lomo,
|
||||
@RequestParam("solapas") Integer solapas,
|
||||
Locale locale) {
|
||||
|
||||
Presupuesto.TipoEncuadernacion tipoEncuadernacion = Presupuesto.TipoEncuadernacion.valueOf(tipoLibro);
|
||||
Presupuesto.TipoCubierta tipoCubierta = Presupuesto.TipoCubierta.valueOf(tapa);
|
||||
|
||||
byte[] png = presupuestoService.obtenerPlantillaCubierta(tipoEncuadernacion, tipoCubierta, ancho, alto, lomo,
|
||||
solapas, locale);
|
||||
if (png == null)
|
||||
return ResponseEntity.notFound().build();
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.noCache())
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.body(png);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/comentario")
|
||||
@ResponseBody
|
||||
public String actualizarComentario(@PathVariable Long id,
|
||||
@ -952,18 +980,17 @@ public class PresupuestoController {
|
||||
@PostMapping("/api/duplicar/{id}")
|
||||
@ResponseBody
|
||||
public Map<String, Object> duplicarPresupuesto(
|
||||
@PathVariable Long id,
|
||||
@RequestParam(name = "titulo", defaultValue = "") String titulo) {
|
||||
|
||||
@PathVariable Long id,
|
||||
@RequestParam(name = "titulo", defaultValue = "") String titulo) {
|
||||
|
||||
Long entity = presupuestoService.duplicarPresupuesto(id, titulo);
|
||||
return Map.of("id", entity);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/api/reimprimir/{id}")
|
||||
@ResponseBody
|
||||
public Map<String, Object> reimprimirPresupuesto(@PathVariable Long id) {
|
||||
|
||||
|
||||
Long entity = presupuestoService.reimprimirPresupuesto(id);
|
||||
return Map.of("id", entity);
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -42,6 +44,15 @@ import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesReposito
|
||||
import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository;
|
||||
import com.imprimelibros.erp.users.UserDao;
|
||||
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.image.PNGTranscoder;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||
@ -1300,7 +1311,7 @@ public class PresupuestoService {
|
||||
public Map<String, Object> obtenerLomos(Presupuesto presupuesto) {
|
||||
try {
|
||||
Map<String, Object> response = apiClient.getLomos(this.toSkApiRequest(presupuesto));
|
||||
|
||||
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error obteniendo lomos: " + e.getMessage());
|
||||
@ -1308,6 +1319,65 @@ public class PresupuestoService {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] obtenerPlantillaCubierta(
|
||||
Presupuesto.TipoEncuadernacion tipoLibro,
|
||||
Presupuesto.TipoCubierta tapa,
|
||||
Integer ancho,
|
||||
Integer alto,
|
||||
Integer lomo,
|
||||
Integer solapas,
|
||||
Locale locale) {
|
||||
|
||||
try {
|
||||
String plantillaName = "plantilla";
|
||||
if (tipoLibro == Presupuesto.TipoEncuadernacion.grapado) {
|
||||
plantillaName += "-grapado";
|
||||
}
|
||||
if (solapas > 0) {
|
||||
plantillaName += "-solapas";
|
||||
}
|
||||
plantillaName += ".svg";
|
||||
|
||||
Integer sangrado = 5; // mm,
|
||||
if (tapa != Presupuesto.TipoCubierta.tapaBlanda && tipoLibro != Presupuesto.TipoEncuadernacion.grapado) {
|
||||
sangrado = 20;
|
||||
}
|
||||
|
||||
Integer ancho_t = lomo + sangrado * 2 + ancho * 2 + solapas * 2;
|
||||
Integer alto_t = alto + sangrado * 2;
|
||||
|
||||
// 3) Leer SVG template como texto
|
||||
String basePath = "static/assets/images/imprimelibros/presupuestador/templates-cubierta/";
|
||||
String svg = readClasspathText(basePath + plantillaName);
|
||||
|
||||
// 4) Sustituciones {{...}}
|
||||
Map<String, String> vars = new HashMap<>();
|
||||
NumberFormat nf = NumberFormat.getIntegerInstance(locale);
|
||||
|
||||
vars.put("ancho", nf.format(ancho)); // mm o lo que representes
|
||||
vars.put("alto", nf.format(alto));
|
||||
vars.put("ancho_t", nf.format(ancho_t));
|
||||
vars.put("alto_t", nf.format(alto_t));
|
||||
vars.put("lomo_t", nf.format(lomo != null ? lomo : 0));
|
||||
vars.put("solapa_t", nf.format(solapas != null ? solapas : 0));
|
||||
vars.put("sangrado", nf.format(sangrado));
|
||||
vars.put("portada", messageSource.getMessage("presupuesto.plantilla-cubierta.portada", null, locale));
|
||||
vars.put("contraportada",
|
||||
messageSource.getMessage("presupuesto.plantilla-cubierta.contraportada", null, locale));
|
||||
vars.put("lomo", messageSource.getMessage("presupuesto.plantilla-cubierta.lomo", null, locale));
|
||||
vars.put("solapa", messageSource.getMessage("presupuesto.plantilla-cubierta.solapa", null, locale));
|
||||
svg = replaceMustache(svg, vars);
|
||||
|
||||
// 5) Render SVG -> PNG (Batik)
|
||||
svg = sanitizeForBatik(svg);
|
||||
return svgToPng(svg, /* dpi */ 300f);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error obteniendo plantilla de cubierta: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador.
|
||||
*/
|
||||
@ -1551,4 +1621,77 @@ public class PresupuestoService {
|
||||
return ip;
|
||||
}
|
||||
|
||||
private static String readClasspathText(String path) throws IOException {
|
||||
ClassPathResource res = new ClassPathResource(path);
|
||||
try (InputStream in = res.getInputStream()) {
|
||||
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private static String replaceMustache(String svg, Map<String, String> vars) {
|
||||
String out = svg;
|
||||
for (var entry : vars.entrySet()) {
|
||||
out = out.replace("{{" + entry.getKey() + "}}", entry.getValue());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] svgToPng(String svgXml, float dpi) throws Exception {
|
||||
PNGTranscoder t = new PNGTranscoder();
|
||||
|
||||
// Esto SÍ es correcto (convierte unidades a mm según DPI)
|
||||
t.addTranscodingHint(PNGTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, 25.4f / dpi);
|
||||
|
||||
// ❌ NO uses KEY_AOI con null (provoca tu error)
|
||||
// t.addTranscodingHint(PNGTranscoder.KEY_AOI, null);
|
||||
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
TranscoderInput input = new TranscoderInput(new StringReader(svgXml));
|
||||
TranscoderOutput output = new TranscoderOutput(out);
|
||||
t.transcode(input, output);
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizeForBatik(String svg) {
|
||||
String out = svg;
|
||||
|
||||
// 1) Batik: context-stroke/context-fill
|
||||
out = out.replace("context-stroke", "#000");
|
||||
out = out.replace("context-fill", "none");
|
||||
|
||||
// 2) Batik: auto-start-reverse
|
||||
out = out.replace("auto-start-reverse", "auto");
|
||||
|
||||
// 3) Reemplazar markers Triangle*/marker6* -> Arrow2S*
|
||||
out = replaceMarkerAttr(out, "marker-start", "Arrow2Sstart");
|
||||
out = replaceMarkerAttr(out, "marker-end", "Arrow2Send");
|
||||
|
||||
// 4) Lo MISMO pero cuando viene dentro de style="...marker-start:url(#X);..."
|
||||
out = replaceMarkerInStyle(out, "marker-start", "Arrow2Sstart");
|
||||
out = replaceMarkerInStyle(out, "marker-end", "Arrow2Send");
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String replaceMarkerAttr(String svg, String attr, String newId) {
|
||||
// Soporta: marker-start="url(#Triangle-5)" o marker-start='url( #Triangle-5 )'
|
||||
Pattern p = Pattern.compile(
|
||||
"(" + attr
|
||||
+ "\\s*=\\s*)([\"'])\\s*url\\(\\s*#(Triangle[^\\s\\)\"']*|marker6[^\\s\\)\"']*)\\s*\\)\\s*\\2",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
Matcher m = p.matcher(svg);
|
||||
return m.replaceAll("$1$2url(#" + newId + ")$2");
|
||||
}
|
||||
|
||||
private static String replaceMarkerInStyle(String svg, String prop, String newId) {
|
||||
// Soporta dentro de style: marker-start:url(#Triangle-5) (con espacios
|
||||
// opcionales)
|
||||
Pattern p = Pattern.compile(
|
||||
"(" + prop + "\\s*:\\s*)url\\(\\s*#(Triangle[^\\s\\)\"';]*|marker6[^\\s\\)\"';]*)\\s*\\)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
Matcher m = p.matcher(svg);
|
||||
return m.replaceAll("$1url(#" + newId + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user