diff --git a/src/main/java/kz/konturai/parser/service/OpenAiChartService.java b/src/main/java/kz/konturai/parser/service/OpenAiChartService.java index 12c9e0d..6e6487e 100644 --- a/src/main/java/kz/konturai/parser/service/OpenAiChartService.java +++ b/src/main/java/kz/konturai/parser/service/OpenAiChartService.java @@ -185,24 +185,32 @@ public class OpenAiChartService { return "Ты — эксперт по визуализации данных. На основе следующего JSON, сгенерируй полный и валидный SVG-код для диаграммы. " + "SVG должен быть стильным и читаемым, с подписями на русском языке. \n" + - "ВАЖНЫЕ ПРАВИЛА:\n" + - "1. Все значения атрибутов width, height, x, y, r должны быть ПОЛОЖИТЕЛЬНЫМИ числами (>= 0).\n" + - "2. Не добавляй никаких комментариев или объяснений.\n" + - "3. Верни ТОЛЬКО SVG-код, начиная с тега и заканчивая .\n\n" + + "КРИТИЧЕСКИ ВАЖНЫЕ ПРАВИЛА:\n" + + "1. ВСЕ значения атрибутов width, height, x, y, r ДОЛЖНЫ быть СТРОГО ПОЛОЖИТЕЛЬНЫМИ числами (> 0).\n" + + + "2. НИКОГДА не используй отрицательные значения в атрибутах элементов , , и других.\n" + + + "3. Для столбчатых диаграмм: y-координата = (высота_диаграммы - высота_столбца), высота столбца всегда > 0.\n" + + + "4. Проверь каждый элемент перед отправкой - отрицательные значения приведут к ошибке конвертации.\n" + + + "5. Не добавляй никаких комментариев или объяснений.\n" + + "6. Верни ТОЛЬКО SVG-код, начиная с тега и заканчивая .\n\n" + "JSON с данными:\n---\n" + json + "\n---"; } else { return "You are a data visualization expert that generates clean, valid SVG code.\n" + "Based on the following JSON, generate a complete and valid SVG code for a bar chart.\n\n" + - "IMPORTANT RULES:\n" + + "CRITICAL RULES - FOLLOW EXACTLY:\n" + "1. The SVG coordinate system starts with (0,0) in the TOP-LEFT corner. The Y-axis points DOWN.\n" + - "2. Therefore, to draw a bar upwards from the bottom, the 'y' attribute of the must be calculated as (chart_height - bar_height), and the 'height' attribute must ALWAYS be a POSITIVE number.\n" + "2. For bar charts: y-coordinate = (chart_height - bar_height), height attribute must ALWAYS be POSITIVE (> 0).\n" + - "3. All dimensional attributes (width, height, x, y, r) MUST be POSITIVE numbers (>= 0).\n" + - "4. The chart must be visually appealing, with appropriate padding, colors, and English labels.\n" + - "5. Double-check your final SVG code to ensure no height or width attributes are negative.\n" + - "6. Return ONLY the raw SVG code, starting with and ending with . Do not include any explanations or markdown.\n\n" + "3. ALL dimensional attributes (width, height, x, y, r) MUST be POSITIVE numbers (> 0).\n" + + "4. NEVER use negative values in any element attributes - this will cause conversion errors.\n" + + "5. Double-check every , , element for positive values.\n" + + "6. The chart must be visually appealing, with appropriate padding, colors, and English labels.\n" + + "7. Return ONLY the raw SVG code, starting with and ending with . No explanations or markdown.\n\n" + "JSON data:\n---\n" + diff --git a/src/main/java/kz/konturai/parser/util/SvgToPngConverter.java b/src/main/java/kz/konturai/parser/util/SvgToPngConverter.java index d53382c..86b0eaa 100644 --- a/src/main/java/kz/konturai/parser/util/SvgToPngConverter.java +++ b/src/main/java/kz/konturai/parser/util/SvgToPngConverter.java @@ -3,6 +3,8 @@ package kz.konturai.parser.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; @@ -12,6 +14,13 @@ import java.io.InputStream; public class SvgToPngConverter { + // Регулярные выражения для поиска и исправления отрицательных значений + private static final Pattern HEIGHT_PATTERN = Pattern.compile("height\\s*=\\s*[\"']([^\"']*)[\"']"); + private static final Pattern WIDTH_PATTERN = Pattern.compile("width\\s*=\\s*[\"']([^\"']*)[\"']"); + private static final Pattern Y_PATTERN = Pattern.compile("y\\s*=\\s*[\"']([^\"']*)[\"']"); + private static final Pattern X_PATTERN = Pattern.compile("x\\s*=\\s*[\"']([^\"']*)[\"']"); + private static final Pattern R_PATTERN = Pattern.compile("r\\s*=\\s*[\"']([^\"']*)[\"']"); + /** * Преобразует строку с SVG-кодом в массив байт PNG-изображения. * @@ -24,21 +33,24 @@ public class SvgToPngConverter { } try { - // 1. Создаем транскодер для PNG + // 1. Санитизируем SVG код перед конвертацией + String sanitizedSvg = sanitizeSvg(svgCode); + + // 2. Создаем транскодер для PNG PNGTranscoder transcoder = new PNGTranscoder(); - // 2. Устанавливаем входные данные (наш SVG-код) - InputStream inputStream = new ByteArrayInputStream(svgCode.getBytes(StandardCharsets.UTF_8)); + // 3. Устанавливаем входные данные (наш SVG-код) + InputStream inputStream = new ByteArrayInputStream(sanitizedSvg.getBytes(StandardCharsets.UTF_8)); TranscoderInput input = new TranscoderInput(inputStream); - // 3. Устанавливаем, куда будет записан результат (в байтовый массив в памяти) + // 4. Устанавливаем, куда будет записан результат (в байтовый массив в памяти) ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); TranscoderOutput output = new TranscoderOutput(outputStream); - // 4. Запускаем процесс конвертации + // 5. Запускаем процесс конвертации transcoder.transcode(input, output); - // 5. Получаем результат + // 6. Получаем результат outputStream.flush(); return outputStream.toByteArray(); @@ -48,4 +60,70 @@ public class SvgToPngConverter { return null; } } + + /** + * Санитизирует SVG код, исправляя отрицательные значения в атрибутах. + * + * @param svgCode Исходный SVG код + * @return Санитизированный SVG код + */ + private static String sanitizeSvg(String svgCode) { + if (svgCode == null || svgCode.isBlank()) { + return svgCode; + } + + String result = svgCode; + + // Исправляем отрицательные значения height + result = fixNegativeValues(result, HEIGHT_PATTERN, "height"); + + // Исправляем отрицательные значения width + result = fixNegativeValues(result, WIDTH_PATTERN, "width"); + + // Исправляем отрицательные значения y + result = fixNegativeValues(result, Y_PATTERN, "y"); + + // Исправляем отрицательные значения x + result = fixNegativeValues(result, X_PATTERN, "x"); + + // Исправляем отрицательные значения r (радиус) + result = fixNegativeValues(result, R_PATTERN, "r"); + + return result; + } + + /** + * Исправляет отрицательные значения в указанном атрибуте. + * + * @param svgCode SVG код + * @param pattern Регулярное выражение для поиска атрибута + * @param attributeName Название атрибута для логирования + * @return Исправленный SVG код + */ + private static String fixNegativeValues(String svgCode, Pattern pattern, String attributeName) { + Matcher matcher = pattern.matcher(svgCode); + StringBuffer result = new StringBuffer(); + + while (matcher.find()) { + String value = matcher.group(1); + try { + // Пытаемся распарсить значение как число + double numValue = Double.parseDouble(value.trim()); + if (numValue < 0) { + // Заменяем отрицательное значение на 0 + String replacement = matcher.group(0).replace(value, "0"); + matcher.appendReplacement(result, replacement); + System.out.println("Исправлено отрицательное значение " + attributeName + ": " + value + " -> 0"); + } else { + matcher.appendReplacement(result, matcher.group(0)); + } + } catch (NumberFormatException e) { + // Если не удается распарсить как число, оставляем как есть + matcher.appendReplacement(result, matcher.group(0)); + } + } + matcher.appendTail(result); + + return result.toString(); + } } \ No newline at end of file