Неправильный цвет фона на первой странице по какой-то причине - PullRequest
0 голосов
/ 11 февраля 2019

Пример файла: здесь Проблема: я пытаюсь решить, виден ли текст на странице.Чтобы добиться этого, для каждой команды Fill я сохраняю ее путь + цвет, что-то вроде этого:

    public class FillNonZeroRule extends OperatorProcessor {
        @Override
        public final void process(Operator operator, List<COSBase> operands) throws IOException {
            PDGraphicsState gs = getGraphicsState();    
            linePath.setWindingRule(GeneralPath.WIND_NON_ZERO);
            addFillPath(gs.getNonStrokingColor());
            linePath.reset();
        }

        @Override
        public String getName() {
            return "f";
        }
    }

    void addFillPath(PDColor color) {
        filledPaths.put((GeneralPath)linePath.clone(), color);
    }

И так я получаю фон для каждого символа:

private PDColor getCharacterBackgroundColor(TextPosition text) {
        PDColor color = null;           
        for (Map.Entry<GeneralPath, PDColor> filledPath : filledPaths.entrySet()) {
            Vector center = getTextPositionCenterPoint(text);
            if (filledPath.getKey().contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {
                color = filledPath.getValue();                  
            }
        }

        return color;
    }

Также сохраняйте цвета для каждой позиции текста.Затем я пытаюсь определить, совпадает ли этот цвет фона с цветом символов.И что интересно, цвет фона для первой страницы и цвета текста для заголовков (строки вверху с фоном) равны 2301728 (значение int RGB) - что неверно, однако для второй страницы цвет текста равен 2301728, а цвет фона -14145754 (правильно!).Поэтому мой вопрос в том, что вызывает неправильный фон на первой странице ... Заранее спасибо!

Весь класс ниже:

public class PdfToTextInfoConverter extends PDFTextStripper {

    private int rotation = 0;

    private float lowerLeftX = 0;

    private float lowerLeftY = 0;

    private PDPage page = null;

    private GeneralPath linePath;

    private Map<GeneralPath, PDColor> filledPaths;

    private Map<TextPosition, PDColor> nonStrokingColors;

    public PdfToTextInfoConverter(PDDocument pddfDoc) throws IOException {
        addOperator(new SetStrokingColorSpace());
        addOperator(new SetNonStrokingColorSpace());
        addOperator(new SetNonStrokingColorN());
        addOperator(new SetStrokingColor());
        addOperator(new SetNonStrokingColor());
        addOperator(new SetStrokingDeviceGrayColor());
        addOperator(new SetNonStrokingDeviceGrayColor());
        addOperator(new SetStrokingDeviceRGBColor());
        addOperator(new SetNonStrokingDeviceRGBColor());
        addOperator(new SetStrokingDeviceCMYKColor());
        addOperator(new SetNonStrokingDeviceCMYKColor());

        addOperator(new AppendRectangleToPath());
        addOperator(new ClipEvenOddRule());
        addOperator(new ClipNonZeroRule());
        addOperator(new ClosePath());
        addOperator(new CurveTo());
        addOperator(new CurveToReplicateFinalPoint());
        addOperator(new CurveToReplicateInitialPoint());
        addOperator(new EndPath());
        addOperator(new FillEvenOddAndStrokePath());
        addOperator(new FillEvenOddRule());
        addOperator(new FillNonZeroAndStrokePath());
        addOperator(new FillNonZeroRule());
        addOperator(new LineTo());
        addOperator(new MoveTo());
        addOperator(new StrokePath());
        document = pddfDoc;
    }

    public void stripPage(int pageNum, int resolution) throws IOException {
        this.setStartPage(pageNum + 1);
        this.setEndPage(pageNum + 1);
        page = document.getPage(pageNum);
        rotation = page.getRotation();
        linePath = new GeneralPath();
        filledPaths = new LinkedHashMap<>();
        nonStrokingColors = new HashMap<>();    
        Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
        writeText(document, dummy); // This call starts the parsing process and calls writeString repeatedly.
    }

    @Override
    public void processPage(PDPage page) throws IOException {
        PDRectangle pageSize = page.getCropBox();

        lowerLeftX = pageSize.getLowerLeftX();
        lowerLeftY = pageSize.getLowerLeftY();

        super.processPage(page);
    }

    private Integer getCharacterBackgroundColor(TextPosition text) {
        Integer fillColorRgb = null;
        try {           
            for (Map.Entry<GeneralPath, PDColor> filledPath : filledPaths.entrySet()) {
                Vector center = getTextPositionCenterPoint(text);
                if (filledPath.getKey().contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {
                    fillColorRgb = filledPath.getValue().toRGB();                   
                }
            }
        } catch (IOException e) {
            logger.error("Could not convert color to RGB", e);
        }
        return fillColorRgb;
    }

    private int getCharacterColor(TextPosition text) {
        int colorRgb = 0; // assume it's black even if we could not convert to RGB
        try {
            colorRgb = nonStrokingColors.get(text).toRGB();         
        } catch (IOException e) {
            logger.error("Could not convert color to RGB", e);
        }
        return colorRgb;
    }

    @Override
    protected void processTextPosition(TextPosition text) {
        PDGraphicsState gs = getGraphicsState();
        // check opacity for stroke and fill text 
        if (gs.getAlphaConstant() < Constants.EPSILON && gs.getNonStrokeAlphaConstant() < Constants.EPSILON) {
            return;
        }                       

        Vector center = getTextPositionCenterPoint(text);
        Area area = gs.getCurrentClippingPath();
        if (area == null || area.contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {            
            nonStrokingColors.put(text, gs.getNonStrokingColor());
            super.processTextPosition(text);
        }
    }

    @Override
    protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
        for (TextPosition text : textPositions) {           
            Integer characterColor = getCharacterColor(text);
            Integer characterBackgroundColor = getCharacterBackgroundColor(text);
        }
    }

    private Vector getTextPositionCenterPoint(TextPosition text) {
        Matrix textMatrix = text.getTextMatrix();
        Vector start = textMatrix.transform(new Vector(0, 0));
        Vector center = null;
        switch (rotation) {
        case 0:
            center = new Vector(start.getX() + text.getWidth()/2, start.getY()); 
            break;
        case 90:
            center = new Vector(start.getX(), start.getY() + text.getWidth()/2);
            break;
        case 180:
            center = new Vector(start.getX() - text.getWidth()/2, start.getY());
            break;
        case 270:
            center = new Vector(start.getX(), start.getY() - text.getWidth()/2);
            break;
        default:
            center = new Vector(start.getX() + text.getWidth()/2, start.getY());
            break;
        }

        return center;
    }

    void addFillPath(PDColor color) {
        filledPaths.put((GeneralPath)linePath.clone(), color);
    }
}

1 Ответ

0 голосов
/ 13 февраля 2019

Это ошибка в PDFBox.

(Ну, в вашем коде также есть проблема, но причина этой проблемы - в PDFBox.)

Ошибка

Проблема в том, что вызов PDColor.toRGB() в

fillColorRgb = filledPath.getValue().toRGB();

повреждает само значение цвета для конкретного рассматриваемого цвета!

Рассматриваемое цветовое пространство является разделительным цветомпространство.Таким образом, PDColor.toRGB() вызывает PDSeparation.toRGB(float[]), используя в качестве параметра его член components.

Если значение RGB для данного параметра еще не находится в кэше в цветовом пространстве, PDSeparation.toRGB(float[]) оценивает tintTransform для заданных параметров.Для рассматриваемого цветового пространства преобразование оттенка является экземпляром PDFunctionType0.Таким образом, вызывается PDFunctionType0.eval(float[]).

К сожалению PDFunctionType0.eval(float[]) предполагает, что он может использовать параметр массива input для своих собственных целей:

input[i] = clipToRange(input[i], domain.getMin(), domain.getMax());
input[i] = interpolate(input[i], domain.getMin(), domain.getMax(), 
        encodeValues.getMin(), encodeValues.getMax());
input[i] = clipToRange(input[i], 0, sizeValues[i] - 1);

Но этот массив является исходным PDColor участник components.Таким образом, эта оценка изменяет один компонент вашего цветового объекта с 0,172 до 43,688.

Позже toRGB требует, чтобы этот цвет нашел 43,688 (или другое значение из-за дальнейших нежелательных изменений), что намного превышает максимальное значение1.0, поэтому они обрезают его до 1.0 и преобразуют оттуда.Но цвет в этом цветовом пространстве с компонентом 1.0 - это именно тот цвет, который используется для текста переднего плана.Таким образом, ваш код думает, что фон и передний план - это одно и то же.

Обходное решение

Чтобы исправить эту проблему, необходимо переписать метод PDFunctionType0.eval(float[]), чтобы он не записывался в массив параметров.Быстрый способ сделать это - добавить

input = input.clone();

в начало этого метода PDFunctionType0.eval(float[]).

Проблема в вашем коде

Ваш метод getTextPositionCenterPointиспользует поворот страницы для определения центра базовой линии заданного TextPosition.Это верно только для текста, который после поворота страницы рисуется вертикально.В случае вашего документа, однако, это не так.Таким образом, вместо поворота страницы вам нужно проанализировать текстовую матрицу для определения фактического направления текста.

Это не будет иметь большого значения в вашем случае, потому что значение TextPosition.getWidth(), которое вы используетеширина символов также рассчитывается на основе поворота страницы.Поскольку рассматриваемая страница не поворачивается, а направление текста поворачивается на 90 °, TextPosition.getWidth() всегда возвращает 0 ... Возможно, вы захотите работать с getWidthDirAdj() вместо ...

...