class Word extends ParsedTextImpl {

    /**
     * Is this an indivisible fragment, because it contained a space or was split from a space- containing string.
     * Non-splittable words can be merged (into new non-splittable words).
     */
    private final boolean shouldNotSplit;
    /**
     * If this word or fragment was preceded by a space, or a line break, it should never be merged into a preceding
     * word.
     */
    private final boolean breakBefore;

    /**
     * @param text           text content
     * @param ascent         font ascent (e.g. height)
     * @param descent        How far below the baseline letters go
     * @param startPoint     first point of the text
     * @param endPoint       ending offset of text
     * @param baseline       line along which text is set.
     * @param spaceWidth     how much space is a space supposed to take.
     * @param isCompleteWord word should never be split
     * @param breakBefore    word starts here, should never combine to the left.
     */
    Word(String text, float ascent, float descent, Vector startPoint,
            Vector endPoint, Vector baseline, float spaceWidth, boolean isCompleteWord, boolean breakBefore) {
        super(text, startPoint, endPoint, baseline, ascent, descent, spaceWidth);
        shouldNotSplit = isCompleteWord;
        this.breakBefore = breakBefore;
    }

    private static String formatPercent(float f) {
        return String.format("%.2f%%", f);
    }

    private static String escapeHTML(String s) {
        return s.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
                .replaceAll(">", "&gt;");
    }

    /**
     * accept a visitor that is assembling text
     *
     * @param p           the assembler that is visiting us.
     * @param contextName What is the wrapping markup element name if any
     * @see com.lowagie.text.pdf.parser.ParsedTextImpl#accumulate(com.lowagie.text.pdf.parser.TextAssembler, String)
     * @see com.lowagie.text.pdf.parser.TextAssemblyBuffer#accumulate(com.lowagie.text.pdf.parser.TextAssembler, String)
     */
    @Override
    public void accumulate(TextAssembler p, String contextName) {
        p.process(this, contextName);
    }

    /**
     * Accept a visitor that is assembling text
     *
     * @param p the assembler that is visiting us.
     * @see com.lowagie.text.pdf.parser.TextAssemblyBuffer#assemble(com.lowagie.text.pdf.parser.TextAssembler)
     * @see com.lowagie.text.pdf.parser.ParsedTextImpl#assemble(com.lowagie.text.pdf.parser.TextAssembler)
     */
    @Override
    public void assemble(TextAssembler p) {
        p.renderText(this);
    }

    /**
     * Generate markup for this word. send the assembler a strings representing a CSS style that will format us nicely.
     *
     * @param text      passed in because we may have wanted to alter it, e.g. by trimming white space, or filtering
     *                  characters or something.
     * @param reader    the file reader from which we are extracting
     * @param page      number of the page we are reading text from
     * @param assembler object to assemble text from fragments and larger strings on a page.
     * @return markup to represent this one word.
     */
    private String wordMarkup(String text, PdfReader reader, int page, TextAssembler assembler) {
        if (text == null) {
            return "";
        }
        Rectangle mediaBox = reader.getPageSize(page);
        Rectangle cropBox = reader.getBoxSize(page, "crop");
        text = text.replaceAll("[\u00A0\u202f]", " ").trim();
        if (text.length() == 0) {
            return text;
        }
        mediaBox.normalize();
        if (cropBox != null) {
            cropBox.normalize();
        } else {
            cropBox = reader.getBoxSize(page, "trim");
            if (cropBox != null) {
                cropBox.normalize();
            } else {
                cropBox = mediaBox;
            }
        }
        float xOffset = cropBox.getLeft() - mediaBox.getLeft();
        float yOffset = cropBox.getTop() - mediaBox.getTop();
        Vector startPoint = getStartPoint();
        Vector endPoint = getEndPoint();
        float pageWidth = cropBox.getWidth();
        float pageHeight = cropBox.getHeight();
        float leftPercent = (float) ((startPoint.get(0) - xOffset - mediaBox.getLeft()) / pageWidth * 100.0);
        float bottom = endPoint.get(1) + yOffset - getDescent() - mediaBox.getBottom();
        float bottomPercent = bottom / pageHeight * 100f;
        StringBuilder result = new StringBuilder();
        float width = getWidth();
        float widthPercent = width / pageWidth * 100.0f;

        float height = getAscent();
        float heightPercent = height / pageHeight * 100.0f;
        String myId = assembler.getWordId();
        Rectangle resultRect = new Rectangle(leftPercent, bottomPercent, leftPercent + widthPercent,
                bottomPercent + heightPercent);
        result.append("<span class=\"t-word\" style=\"bottom: ")
                .append(formatPercent(resultRect.getBottom())).append("; left: ")
                .append(formatPercent(resultRect.getLeft())).append("; width: ")
                .append(formatPercent(resultRect.getWidth())).append("; height: ")
                .append(formatPercent(resultRect.getHeight())).append(";\"")
                .append(" id=\"").append(myId).append("\">")
                .append(escapeHTML(text)).append(" ");
        result.append("</span> ");

        return result.toString();
    }

    /**
     * @see com.lowagie.text.pdf.parser.TextAssemblyBuffer#getFinalText(PdfReader, int, TextAssembler, boolean)
     */
    @Override
    public FinalText getFinalText(PdfReader reader, int page,
            TextAssembler assembler, boolean useMarkup) {<FILL_FUNCTION_BODY>}

    @Override
    public String toString() {
        return "[Word: [" + getText() + "] " + getStartPoint() + ", "
                + getEndPoint() + "] lead" + getAscent() + "]";
    }

    /**
     * @see com.lowagie.text.pdf.parser.ParsedTextImpl#shouldNotSplit()
     */
    @Override
    public boolean shouldNotSplit() {
        return shouldNotSplit;
    }

    /**
     * @see com.lowagie.text.pdf.parser.ParsedTextImpl#breakBefore()
     */
    @Override
    public boolean breakBefore() {
        return breakBefore;
    }
}

        final String text = getText() == null ? "" : getText();
        if (useMarkup) {
            return new FinalText(wordMarkup(text, reader, page, assembler));
        } else {
            final boolean hasSpaceAlready = text.startsWith(" ") || text.endsWith(" ");
            String prefixSpace = hasSpaceAlready ? "" : " ";
            return new FinalText(prefixSpace + text);
        }
    
<methods>public abstract boolean breakBefore() ,public float getAscent() ,public com.lowagie.text.pdf.parser.Vector getBaseline() ,public float getDescent() ,public com.lowagie.text.pdf.parser.Vector getEndPoint() ,public float getSingleSpaceWidth() ,public com.lowagie.text.pdf.parser.Vector getStartPoint() ,public java.lang.String getText() ,public float getWidth() ,public abstract boolean shouldNotSplit() <variables>private float ascent,private com.lowagie.text.pdf.parser.Vector baseline,private float descent,private com.lowagie.text.pdf.parser.Vector endPoint,private float spaceWidth,private com.lowagie.text.pdf.parser.Vector startPoint,private final non-sealed java.lang.String text