Decoration.java 11.8 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.xfa.text;

import com.adobe.xfa.font.FontInstance;
import com.adobe.xfa.gfx.GFXDecorationInfo;
import com.adobe.xfa.text.DispLineWrapped;
import com.adobe.xfa.text.DrawParm;
import com.adobe.xfa.text.LineHeight;
import com.adobe.xfa.text.TextAttr;
import com.adobe.xfa.text.TextBaselineShift;
import com.adobe.xfa.text.Units;
import com.adobe.xfa.ut.UnitSpan;
import java.util.Map;

abstract class Decoration {
    static final int SPECIAL_HONOUR_SUPERSCRIPT = 0;
    static final int SPECIAL_HONOUR_SUBSCRIPT = 1;
    static final int SPECIAL_BREAK_ON_ALL_CHANGES = 2;
    static final int DECORATION_SPACE = 1;
    static final int DECORATION_SUPPRESS = 2;
    static final int DECORATION_VERTICAL = 4;
    private int mnType = 1;
    private int mnCount = 0;
    private int meSpecial;
    private double mdWidth;
    private double mdOffset;
    private double mdOffset2;
    private boolean mbUseDescent;
    private UnitSpan moBaseline;
    private float moStart;
    private float moNonSpace;
    private float moSpace;
    private final LineHeight moHeight = new LineHeight();

    static Decoration createUnderline(DispLineWrapped line, DrawParm parm) {
        return new Underline(line, parm);
    }

    static Decoration createLineThrough(DispLineWrapped line, DrawParm parm) {
        return new LineThrough(line, parm);
    }

    static Decoration createOverline(DispLineWrapped line, DrawParm parm) {
        return new Overline(line, parm);
    }

    void update(DrawParm oParm, TextAttr poAttr, float oLeft, float oRight, int nFlags, Map<DecorationKey, DecorationValue> oDecorations) {
        boolean bBreak = false;
        int nCount = 0;
        int nType = 1;
        if (poAttr != null) {
            GFXDecorationInfo info = GFXDecorationInfo.extractDecoration(this.getLineState(poAttr));
            nCount = info.mCount;
            nType = info.mType;
        }
        boolean bIsSpace = (nFlags & 1) != 0;
        FontInstance poFontInstance = null;
        if (poAttr != null) {
            poFontInstance = poAttr.fontInstance();
        }
        if (nType == 3) {
            nType = bIsSpace ? 1 : 2;
        }
        UnitSpan oNewShift = oParm.offsetText();
        if (poAttr != null && poAttr.baselineShiftEnable() && !poAttr.baselineShift().isNeutral()) {
            TextBaselineShift oShifter = poAttr.baselineShift();
            oNewShift = oShifter.applyShift(oParm.offsetText(), oParm.offsetText());
            switch (this.meSpecial) {
                case 0: {
                    if (!oNewShift.gt(this.moBaseline)) break;
                    oNewShift = this.moBaseline;
                    break;
                }
                case 1: {
                    if (!oNewShift.lt(this.moBaseline)) break;
                    oNewShift = this.moBaseline;
                }
            }
        }
        if (this.meSpecial == 2 && oNewShift != this.moBaseline) {
            bBreak = true;
        }
        if ((nFlags & 2) != 0) {
            nType = 1;
            nCount = 0;
        }
        if (nType != this.mnType || nCount != this.mnCount) {
            bBreak = true;
        } else if (this.meSpecial == 2 && poFontInstance != null && !poFontInstance.getAscent().equals(Units.toUnitSpan(this.moHeight.ascent()))) {
            bBreak = true;
        }
        if (bBreak) {
            this.flush(oParm, true, oDecorations);
            this.mnType = nType;
            this.mnCount = nCount;
            this.onLineStateChange();
            this.moStart = oLeft;
        }
        if (nType == 1) {
            return;
        }
        this.moHeight.accumulate(poAttr, (nFlags & 4) != 0 ? 1 : 0, false);
        this.moBaseline = oNewShift;
        if (bIsSpace) {
            this.moSpace = oRight;
        } else {
            this.moNonSpace = oRight;
        }
    }

    void fontChange(DrawParm oParm, Map<DecorationKey, DecorationValue> oDecorations) {
        if (this.meSpecial == 2) {
            this.flush(oParm, true, oDecorations);
        }
    }

    void complete(DrawParm oParm, Map<DecorationKey, DecorationValue> oDecorations, boolean bIncludeSpaces) {
        this.flush(oParm, bIncludeSpaces, oDecorations);
    }

    static boolean hasDecoration(TextAttr poAttr) {
        if (poAttr == null) {
            return false;
        }
        return Decoration.hasDecoration(poAttr.underline()) || Decoration.hasDecoration(poAttr.overline()) || Decoration.hasDecoration(poAttr.strikeout());
    }

    static boolean hasDecoration(int nDecoration) {
        return GFXDecorationInfo.extractDecoration(nDecoration) != GFXDecorationInfo.decorateNone;
    }

    Decoration(DispLineWrapped poLine, DrawParm oParm) {
        this.moBaseline = oParm.offsetText();
        this.moHeight.setLegacyLevel(poLine.getLegacyLevel());
    }

    void setSpecial(int eSpecial) {
        this.meSpecial = eSpecial;
    }

    void setUseDescent(boolean bUseDescent) {
        this.mbUseDescent = bUseDescent;
    }

    void setFactors(double dWidth, double dOffset, double dOffset2) {
        this.mdWidth = dWidth;
        if (this.mbUseDescent) {
            this.mdOffset = dOffset;
            this.mdOffset2 = dOffset2;
        } else {
            this.mdOffset = - dOffset;
            this.mdOffset2 = - dOffset2;
        }
    }

    void setFactors(double dWidth, double dOffset) {
        this.setFactors(dWidth, dOffset, 0.0);
    }

    int getCount() {
        return this.mnCount;
    }

    abstract int getLineState(TextAttr var1);

    abstract void onLineStateChange();

    private void flush(DrawParm oParm, boolean bIncludeSpaces, Map<DecorationKey, DecorationValue> oDecorations) {
        if (this.mnType != 1) {
            float oEnd = this.moNonSpace;
            if (bIncludeSpaces && this.moSpace > this.moNonSpace) {
                oEnd = this.moSpace;
            }
            if (oEnd > this.moStart) {
                UnitSpan oScale = null;
                if (this.mbUseDescent) {
                    oScale = Units.toUnitSpan(this.moHeight.descent());
                    if (oScale.value() == 0) {
                        oScale = new UnitSpan((double)this.moHeight.ascent() / 3.0, 19);
                    }
                } else {
                    oScale = Units.toUnitSpan(this.moHeight.ascent());
                }
                DecorationKey oKey = new DecorationKey(this.moStart, oEnd);
                UnitSpan width = oScale.multiply(this.mdWidth);
                UnitSpan halfWidth = width.divide(2);
                UnitSpan yOffset = this.calculateOffset(oScale, this.mdOffset, halfWidth);
                oDecorations.put(oKey, new DecorationValue(yOffset, width));
                if (this.mnCount == 2) {
                    UnitSpan yOffset2 = this.calculateOffset(oScale, this.mdOffset2, halfWidth);
                    oDecorations.put(oKey, new DecorationValue(yOffset2, width));
                }
            }
        }
        this.mnType = 1;
        this.mnCount = 0;
        this.moHeight.reset();
        this.moBaseline = oParm.offsetText();
    }

    private UnitSpan calculateOffset(UnitSpan scale, double offset, UnitSpan halfWidth) {
        UnitSpan scaledOffset = scale.multiply(offset);
        UnitSpan quarterWidth = halfWidth.divide(2);
        UnitSpan relativeOffset = scaledOffset.add(quarterWidth);
        return this.moBaseline.add(relativeOffset);
    }

    private static class Overline
    extends Decoration {
        private static final double SINGLE_OFFSET = 1.1;
        private static final double SINGLE_WIDTH = 0.04;
        private static final double DOUBLE_OFFSET_1 = 1.1;
        private static final double DOUBLE_OFFSET_2 = 1.18;
        private static final double DOUBLE_WIDTH = 0.3;

        Overline(DispLineWrapped poLine, DrawParm oParm) {
            super(poLine, oParm);
            this.setSpecial(0);
        }

        @Override
        int getLineState(TextAttr poAttr) {
            return poAttr == null ? 0 : poAttr.overline();
        }

        @Override
        void onLineStateChange() {
            switch (this.getCount()) {
                case 1: {
                    this.setFactors(0.04, 1.1);
                    break;
                }
                case 2: {
                    this.setFactors(0.3, 1.1, 1.18);
                }
            }
        }
    }

    private static class LineThrough
    extends Decoration {
        private static final double SINGLE_OFFSET = 0.4;
        private static final double SINGLE_WIDTH = 0.04;
        private static final double DOUBLE_OFFSET_1 = 0.36;
        private static final double DOUBLE_OFFSET_2 = 0.44;
        private static final double DOUBLE_WIDTH = 0.03;

        LineThrough(DispLineWrapped poLine, DrawParm oParm) {
            super(poLine, oParm);
            this.setSpecial(2);
        }

        @Override
        int getLineState(TextAttr poAttr) {
            return poAttr == null ? 0 : poAttr.strikeout();
        }

        @Override
        void onLineStateChange() {
            switch (this.getCount()) {
                case 1: {
                    this.setFactors(0.04, 0.4);
                    break;
                }
                case 2: {
                    this.setFactors(0.03, 0.36, 0.44);
                }
            }
        }
    }

    private static class Underline
    extends Decoration {
        private static final double SINGLE_OFFSET = 0.4;
        private static final double SINGLE_WIDTH = 0.16;
        private static final double DOUBLE_OFFSET_1 = 0.4;
        private static final double DOUBLE_OFFSET_2 = 0.9;
        private static final double DOUBLE_WIDTH = 0.08;

        Underline(DispLineWrapped poLine, DrawParm oParm) {
            super(poLine, oParm);
            this.setSpecial(1);
            this.setUseDescent(true);
        }

        @Override
        int getLineState(TextAttr poAttr) {
            return poAttr == null ? 0 : poAttr.underline();
        }

        @Override
        void onLineStateChange() {
            switch (this.getCount()) {
                case 1: {
                    this.setFactors(0.16, 0.4);
                    break;
                }
                case 2: {
                    this.setFactors(0.08, 0.4, 0.9);
                }
            }
        }
    }

    static class DecorationValue {
        final UnitSpan moYOffset;
        final UnitSpan moWidth;

        public DecorationValue(UnitSpan yOffset, UnitSpan width) {
            this.moYOffset = yOffset;
            this.moWidth = width;
        }
    }

    static class DecorationKey
    implements Comparable<DecorationKey> {
        final float moXStart;
        final float moXEnd;

        public DecorationKey(float xStart, float xEnd) {
            this.moXStart = xStart;
            this.moXEnd = xEnd;
        }

        @Override
        public int compareTo(DecorationKey compare) {
            if (compare == null) {
                throw new NullPointerException();
            }
            if (this.moXStart < compare.moXStart) {
                return -1;
            }
            if (this.moXStart > compare.moXStart) {
                return 1;
            }
            if (this.moXEnd < compare.moXEnd) {
                return -1;
            }
            if (this.moXEnd > compare.moXEnd) {
                return 1;
            }
            return 0;
        }

        public boolean equals(Object object) {
            if (this == object) {
                return true;
            }
            if (object == null) {
                return false;
            }
            if (object.getClass() != this.getClass()) {
                return false;
            }
            return this.compareTo((DecorationKey)object) == 0;
        }

        public int hashCode() {
            int hash = 13;
            hash = hash * 31 ^ Float.floatToIntBits(this.moXStart);
            hash = hash * 31 ^ Float.floatToIntBits(this.moXEnd);
            return hash;
        }
    }

}