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 . 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 . 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