Из SVG в PNG с помощью Batik: проблема с многострочным текстом

Я конвертирую SVG в изображение PNG с помощью Batik, но текст, который идет по нескольким строкам, отображается неправильно. Недетерминированным образом может случиться так, что сам текст больше не будет центрирован на конечном изображении.

В качестве примера рассмотрим эту часть стартового SVG:

введите здесь описание изображения

Это правильный рендеринг, который я хочу получить и в PNG. Полученный PNG, кстати, выглядит так:

введите здесь описание изображения

Как видите, текст в верхнем зеленом поле больше не центрирован.

Это часть SVG, которая определяет это (подозревая проблему со шрифтами, я также попытался удалить все font-family="", но безрезультатно):

  <g transform="translate(184.68 -0.600364)">
     <text fill="rgb(255, 255, 255)" fill-opacity="1" font-family="Kievit Pro" font-size="10px" font-style="normal" font-weight="normal" text-anchor="middle" transform="translate(48 0.429996)">
        <tspan dy="1em" x="0">A simple activity, with</tspan>
        <tspan dy="1.5em" x="0">a text that goes on t</tspan>
     </text>
  </g>

Если это может помочь, это код, который я использую для преобразования:

static byte[] convertToPng(byte[] byteSvgSmall) throws TranscoderException, IOException {
    if (byteSvgSmall != null) {

        byte[] byteImagePng = null;
        ByteArrayInputStream bais = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        InputStream targetStream = null;

        try {
            targetStream = new ByteArrayInputStream(byteSvgSmall);
            TranscoderInput input = new TranscoderInput(targetStream);

            PNGTranscoder transcoder = new PNGTranscoder();
            transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(diagramWidthInPixelsForPdf));

            TranscoderOutput output = new TranscoderOutput(baos);
            transcoder.transcode(input, output);

            byte[] byteOutput = baos.toByteArray();
            byteImagePng = new byte[byteOutput.length];
            bais = new ByteArrayInputStream(baos.toByteArray());
            bais.read(byteImagePng);
            targetStream.close();
            baos.flush();
            return byteImagePng;
        } catch (Exception e) {
            BeanFactory
                    .getLogger()
                    .error(SVGTransformer.class,
                            "An error occurred. A null byte[] will be returned",
                            e);
            return null;
        } finally {
            if (bais != null) bais.close();
            if (baos != null) baos.close();
            if (targetStream != null) targetStream.close();
        }
    }
    return null;
}

И затем с возвращенным byte[]:

if (pngBytes != null) {
   BufferedImage final_img = ImageIO.read(new ByteArrayInputStream(pngBytes));
   File output_file = new File(imagepath);
   ImageIO.write(final_img, "png", output_file);
}

Большое спасибо за любое понимание.


person Andrea    schedule 02.03.2018    source источник
comment
@Andreaジーティーオー Мне не удалось воспроизвести проблему. Я использую 1.9.1 ‹!-- mvnrepository.com/artifact/org .apache.xmlgraphics/batik-all --› ‹dependency› ‹groupId›org.apache.xmlgraphics‹/groupId› ‹artifactId›batik-all‹/artifactId› ‹version›1.9.1‹/version› ‹тип›pom‹/тип› ‹/зависимость›   -  person Ezekiel Baniaga    schedule 08.03.2018
comment
@EzekielBaniaga: Большое спасибо за вашу попытку, очень признателен. Отвечая также Дейву Джарвису, я компилирую с помощью Java 1.6, batik-all-1.7.jar; Я не знаю, какой инструмент генерирует SVG; Я получаю его от другой команды, чтобы поместить в PDF с Apache POI.   -  person Andrea    schedule 08.03.2018


Ответы (2)


Я загрузил ваш svg, и файл (по крайней мере, на моем компьютере) не совсем соответствует тексту в пределах зеленых полей (есть небольшое переполнение, которое обрезается). Я не уверен, что это проблема платформы, поскольку на вашем снимке экрана текст показан с небольшим запасом (тогда как в моем случае при открытии с помощью GIMP текст выходит за его пределы).

Таким образом, я изменил ваш svg так, чтобы все размеры шрифта 10 пикселей были уменьшены до 9 пикселей (чтобы все поместилось в квадратах).

Затем я запустил ваш код с помощью java-8-openjdk-amd64, предоставленного svg и следующих зависимостей:

compile group: 'org.apache.xmlgraphics', name: 'batik-transcoder', version: '1.9.1'
compile group: 'org.apache.xmlgraphics', name: 'xmlgraphics-commons', version: '2.2'
compile group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.9.1'

Поле TEST1 в моем png похоже, но не идентично вашему:

A simple activity, with
  a text that goes on t

Затем я изменил узел xml, изменив вторую строку с «текст, который идет на t» на «текст, который». На этот раз вторая линия была в центре.

A simple activity, with
     a text that

Затем я вернул исходный текст в узел и изменил семейство шрифтов для поля на Lucida Sans и размер на 7 пикселей (мой модифицированный узел здесь ниже).

<text fill="rgb(255, 255, 255)" fill-opacity="1" font-family="Lucida Sans" font-size="7px" font-style="normal" font-weight="normal" text-anchor="middle" transform="translate(48 0.429996)">
    <tspan dy="1em" x="0">A simple activity, with</tspan>
    <tspan dy="1.5em" x="0">a text that goes on t</tspan>
</text>

На этот раз по центру. Что вы пробовали? Какие результаты вы получите, если попробуете вышеописанное (изменив размер шрифта и/или семейство шрифтов)? Я также использовал 1000px для подсказки транскодирования KEY_WIDTH, если это может что-то изменить в вашем случае.

person geco17    schedule 07.03.2018
comment
Привет, Уильям, спасибо, очень ценю. Возможно, на вашем компьютере текст отображается по-другому из-за отсутствия шрифта Kievit Pro. Даже в моем случае окончательный рендеринг немного отличается и, кажется, теряет этот шрифт при преобразовании (как во втором изображении моего вопроса: это результирующий png, помещенный в pdf с Apache POI). Итак, если я правильно понял, ваш совет - уменьшить размер шрифта с помощью массовой замены. Я стараюсь и дам вам знать. Возможно, Batik встречает текст, который слишком длинный для своего контейнера, и имеет проблемы с его рендерингом. - person Andrea; 08.03.2018
comment
Привет, да, я предлагаю попробовать разные значения font-family и/или font-size. Я обновил ответ соответственно. Это немного случайно, так как я не смог воспроизвести ваше точное начальное условие, но это похоже на проблему рендеринга в библиотеке. Я не уверен, что это за шрифт Kevit Pro, но Lucida Sans должен быть дружественным к Java. Дайте мне знать, как это происходит. - person geco17; 08.03.2018
comment
да, я не могу быть в этом уверен, потому что я не знаю, что делает Батик под капотом. Но изменение размера шрифта непосредственно перед преобразованием, похоже, решает проблему. Возможно, действительно существует проблема с размещением текста в контейнере, который больше не помещается в него. Я думаю, что следующим шагом будет попытка сохранить семейство шрифтов в преобразовании. На данный момент... большое спасибо (а также всем, кто пытался помочь)! - person Andrea; 09.03.2018