class PdfString extends PdfObject {

    // CLASS VARIABLES

    /**
     * The value of this object.
     */
    protected String value = NOTHING;

    protected String originalValue = null;

    /**
     * The encoding.
     */
    protected String encoding = TEXT_PDFDOCENCODING;

    protected int objNum = 0;

    protected int objGen = 0;

    protected boolean hexWriting = false;

    // CONSTRUCTORS

    /**
     * Constructs an empty <CODE>PdfString</CODE>-object.
     */
    public PdfString() {
        super(STRING);
    }

    /**
     * Constructs a <CODE>PdfString</CODE>-object containing a string in the standard encoding
     * <CODE>TEXT_PDFDOCENCODING</CODE>.
     *
     * @param value the content of the string
     */
    public PdfString(String value) {
        super(STRING);
        this.value = value;
    }

    /**
     * Constructs a <CODE>PdfString</CODE>-object containing a string in the specified encoding.
     *
     * @param value    the content of the string
     * @param encoding an encoding
     */
    public PdfString(String value, String encoding) {
        super(STRING);
        this.value = value;
        this.encoding = encoding;
    }

    /**
     * Constructs a <CODE>PdfString</CODE>-object.
     *
     * @param bytes an array of <CODE>byte</CODE>
     */
    public PdfString(byte[] bytes) {
        super(STRING);
        value = PdfEncodings.convertToString(bytes, null);
        encoding = NOTHING;
    }

    // methods overriding some methods in PdfObject

    /**
     * Writes the PDF representation of this <CODE>PdfString</CODE> as an array of <CODE>byte</CODE> to the specified
     * <CODE>OutputStream</CODE>.
     *
     * @param writer for backwards compatibility
     * @param os     The <CODE>OutputStream</CODE> to write the bytes to.
     */
    public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
        byte[] b = getBytes();
        PdfEncryption crypto = null;
        if (writer != null) {
            crypto = writer.getEncryption();
        }
        if (crypto != null && !crypto.isEmbeddedFilesOnly()) {
            b = crypto.encryptByteArray(b);
        }
        if (hexWriting) {
            ByteBuffer buf = new ByteBuffer();
            buf.append('<');
            int len = b.length;
            for (byte b1 : b) {
                buf.appendHex(b1);
            }
            buf.append('>');
            os.write(buf.toByteArray());
        } else {
            os.write(PdfContentByte.escapeString(b));
        }
    }

    /**
     * Returns the <CODE>String</CODE> value of this <CODE>PdfString</CODE>-object.
     *
     * @return A <CODE>String</CODE>
     */
    public String toString() {
        return value;
    }

    public byte[] getBytes() {
        if (bytes == null) {
            if (encoding != null && encoding.equals(TEXT_UNICODE) && PdfEncodings.isPdfDocEncoding(value)) {
                bytes = PdfEncodings.convertToBytes(value, TEXT_PDFDOCENCODING);
            } else {
                bytes = PdfEncodings.convertToBytes(value, encoding);
            }
        }
        return bytes;
    }

    // other methods

    /**
     * Returns the Unicode <CODE>String</CODE> value of this
     * <CODE>PdfString</CODE>-object.
     *
     * @return A <CODE>String</CODE>
     */
    public String toUnicodeString() {
        if (encoding != null && encoding.length() != 0) {
            return value;
        }
        getBytes();
        if (bytes.length >= 2 && bytes[0] == (byte) 254 && bytes[1] == (byte) 255) {
            return PdfEncodings.convertToString(bytes, PdfObject.TEXT_UNICODE);
        } else {
            return PdfEncodings.convertToString(bytes, PdfObject.TEXT_PDFDOCENCODING);
        }
    }

    /**
     * Gets the encoding of this string.
     *
     * @return a <CODE>String</CODE>
     */
    public String getEncoding() {
        return encoding;
    }

    void setObjNum(int objNum, int objGen) {
        this.objNum = objNum;
        this.objGen = objGen;
    }

    /**
     * Decrypt an encrypted <CODE>PdfString</CODE>
     */
    void decrypt(PdfReader reader) {
        PdfEncryption decrypt = reader.getDecrypt();
        if (decrypt != null) {
            originalValue = value;
            decrypt.setHashKey(objNum, objGen);
            bytes = PdfEncodings.convertToBytes(value, null);
            bytes = decrypt.decryptByteArray(bytes);
            value = PdfEncodings.convertToString(bytes, null);
        }
    }

    /**
     * @return The original bytes used to create this PDF string, or the bytes of our current value if the original
     * bytes are missing.
     */
    public byte[] getOriginalBytes() {
        if (originalValue == null) {
            return getBytes();
        }
        return PdfEncodings.convertToBytes(originalValue, null);
    }

    /**
     * return the characters in our value without any translation. This allows a string to be built that holds 2-byte or
     * one-byte character codes, as needed for processing by fonts when extracting text.
     * <p>
     * Intended for use when no encoding transformations are desired.
     *
     * @return The code points in this font as chars.
     */
    public char[] getOriginalChars() {<FILL_FUNCTION_BODY>}

    public boolean isHexWriting() {
        return hexWriting;
    }

    public PdfString setHexWriting(boolean hexWriting) {
        this.hexWriting = hexWriting;
        return this;
    }
}

        char[] chars;
        if (encoding == null || encoding.length() == 0) {
            byte[] bytes = getOriginalBytes();
            chars = new char[bytes.length];
            for (int i = 0; i < bytes.length; i++) {
                chars[i] = (char) (bytes[i] & 0xff);
            }
        } else if (encoding.equals("IDENTITY_H2")) {
            //change it to char array according to two byte mapping.
            byte[] bytes = value.getBytes(StandardCharsets.ISO_8859_1);
            chars = new char[bytes.length / 2];
            for (int i = 0; i < bytes.length / 2; i++) {
                chars[i] = (char) (((bytes[2 * i] & 255) << 8) + (bytes[2 * i + 1] & 255));
            }
        } else if (encoding.equals("IDENTITY_H1")) {
            //change it to char array according to one byte mapping.
            byte[] bytes = value.getBytes(StandardCharsets.ISO_8859_1);
            chars = new char[bytes.length];
            for (int i = 0; i < bytes.length; i++) {
                chars[i] = (char) (bytes[i] & 0xff);
            }
        } else {
            chars = new char[0];
        }
        return chars;
    
<methods>public boolean canBeInObjStm() ,public byte[] getBytes() ,public com.lowagie.text.pdf.PRIndirectReference getIndRef() ,public boolean isArray() ,public boolean isBoolean() ,public boolean isDictionary() ,public boolean isIndirect() ,public boolean isName() ,public boolean isNull() ,public boolean isNumber() ,public boolean isStream() ,public boolean isString() ,public int length() ,public void setIndRef(com.lowagie.text.pdf.PRIndirectReference) ,public void toPdf(com.lowagie.text.pdf.PdfWriter, java.io.OutputStream) throws java.io.IOException,public java.lang.String toString() ,public int type() <variables>public static final int ARRAY,public static final int BOOLEAN,public static final int DICTIONARY,public static final int INDIRECT,public static final int NAME,public static final java.lang.String NOTHING,public static final int NULL,public static final int NUMBER,public static final int STREAM,public static final int STRING,public static final java.lang.String TEXT_PDFDOCENCODING,public static final java.lang.String TEXT_UNICODE,protected byte[] bytes,protected com.lowagie.text.pdf.PRIndirectReference indRef,protected int type