All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.icepdf.ri.common.tools.TextSelection Maven / Gradle / Ivy

There is a newer version: 6.2.2
Show newest version
/*
 * Copyright 2006-2017 ICEsoft Technologies Canada Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS
 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.icepdf.ri.common.tools;

import org.icepdf.core.pobjects.Page;
import org.icepdf.core.pobjects.graphics.text.GlyphText;
import org.icepdf.core.pobjects.graphics.text.LineText;
import org.icepdf.core.pobjects.graphics.text.PageText;
import org.icepdf.core.pobjects.graphics.text.WordText;
import org.icepdf.core.util.Defs;
import org.icepdf.core.util.PropertyConstants;
import org.icepdf.ri.common.views.AbstractPageViewComponent;
import org.icepdf.ri.common.views.DocumentViewController;
import org.icepdf.ri.common.views.DocumentViewModel;
import org.icepdf.ri.common.views.PageViewComponentImpl;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.icepdf.ri.common.tools.TextSelection.enableMarginExclusion;

/**
 * TextSelection is a utility class that captures most of the work needed to do basic text, word and line selection.
 */
public class TextSelection extends SelectionBoxHandler {

    protected static final Logger logger =
            Logger.getLogger(TextSelection.class.toString());

    public int selectedCount;

    protected Point lastMousePressedLocation;
    protected Point lastMouseLocation;

    private GlyphLocation glyphStartLocation;
    private GlyphLocation glyphEndLocation;

    private GlyphLocation lastGlyphStartLocation;
    private GlyphLocation lastGlyphEndLocation;

    // todo configurable system property to switch to rightToLeft.
    private boolean leftToRight = true;

    // todo make configurable
    protected int topMargin = 75;
    protected int bottomMargin = 75;
    protected static boolean enableMarginExclusion;
    protected static boolean enableMarginExclusionBorder;
    protected Rectangle2D topMarginExclusion;
    protected Rectangle2D bottomMarginExclusion;

    // Pointer to make sure the GC doesn't collect a page while selection state is present
    protected Page pageLock;


    static {
        try {
            enableMarginExclusion = Defs.booleanProperty(
                    "org.icepdf.core.views.page.marginExclusion.enabled", false);
        } catch (NumberFormatException e) {
            logger.warning("Error reading margin exclusion enabled property.");
        }
        try {
            enableMarginExclusionBorder = Defs.booleanProperty(
                    "org.icepdf.core.views.page.marginExclusionBorder.enabled", false);
        } catch (NumberFormatException e) {
            logger.warning("Error reading margin exclusion boarder enabled property.");
        }
    }

    // first page that was selected
    private boolean isFirst;

    public TextSelection(DocumentViewController documentViewController, AbstractPageViewComponent pageViewComponent,
                         DocumentViewModel documentViewModel) {
        super(documentViewController, pageViewComponent, documentViewModel);
    }

    /**
     * Handles double and triple left mouse clicks to select a word or line of text respectively.
     *
     * @param clickCount number of mouse clicks to interpret for line or word selection.
     * @param clickPoint point that mouse was clicked.
     */
    public void wordLineSelection(int clickCount, Point clickPoint, AbstractPageViewComponent pageViewComponent) {
        // double click we select the whole line.
        try {
            if (clickCount == 3) {
                Page currentPage = pageViewComponent.getPage();
                // handle text selection mouse coordinates
                Point mouseLocation = (Point) clickPoint.clone();
                lineSelectHandler(currentPage, mouseLocation);
            }
            // single click we select word that was clicked.
            else if (clickCount == 2) {
                Page currentPage = pageViewComponent.getPage();
                // handle text selection mouse coordinates
                Point mouseLocation = (Point) clickPoint.clone();
                wordSelectHandler(currentPage, mouseLocation);
            }
            if (pageViewComponent != null) {
                pageViewComponent.requestFocus();
            }
        } catch (InterruptedException e) {
            logger.fine("Text selection page access interrupted");
        }
    }

    /**
     * Selection started so we want to record the position and update the selection rectangle.
     *
     * @param startPoint starting selection position.
     */
    public void selectionStart(Point startPoint, AbstractPageViewComponent pageViewComponent, boolean isFirst) {
        try {
            Page currentPage = pageViewComponent.getPage();
            this.isFirst = isFirst;
            if (currentPage != null) {
                // get page text
                PageText pageText = currentPage.getViewText();
                // get page transform, same for all calculations
                AffineTransform pageTransform = currentPage.getPageTransform(
                        documentViewModel.getPageBoundary(),
                        documentViewModel.getViewRotation(),
                        documentViewModel.getViewZoom());

                // create exclusion boxes
                calculateTextSelectionExclusion(pageTransform);

                ArrayList pageLines = pageText.getPageLines();
                Point2D.Float dragStartLocation = convertMouseToPageSpace(startPoint, pageTransform);
                glyphStartLocation = GlyphLocation.findGlyphLocation(pageLines, dragStartLocation, true, false, null,
                        topMarginExclusion, bottomMarginExclusion);
                glyphEndLocation = null;
            }

            // text selection box.
            currentRect = new Rectangle(startPoint.x, startPoint.y, 0, 0);
            updateDrawableRect(pageViewComponent.getWidth(), pageViewComponent.getHeight());
            pageViewComponent.repaint();
        } catch (InterruptedException e) {
            logger.fine("Text selection page access interrupted");
        }
    }

    /**
     * Selection ended so we want to stop record the position and update the selection.
     */
    public void selectionEnd(Point endPoint, AbstractPageViewComponent pageViewComponent) {

        try {
            // write out selected text.
            if (pageViewComponent != null && logger.isLoggable(Level.FINE)) {
                Page currentPage = pageViewComponent.getPage();
                // handle text selection mouse coordinates
                if (currentPage.getViewText() != null) {
                    logger.fine(currentPage.getViewText().getSelected().toString());
                }
            }

            if (selectedCount > 0) {

                // add the page to the page as it is marked for selection
                documentViewModel.addSelectedPageText(pageViewComponent);
                documentViewController.firePropertyChange(
                        PropertyConstants.TEXT_SELECTED,
                        null, null);
            }

            // clear the rectangle
            clearRectangle(pageViewComponent);

            if (pageViewComponent != null) {
                pageViewComponent.repaint();
            }
        } catch (InterruptedException e) {
            logger.fine("Text selection page access interrupted");
        }
    }

    public void clearSelection() {

        // release the page lock so the Reference API can take care of collecting the page post selection.
        pageLock = null;

        lastGlyphStartLocation = null;
        lastGlyphEndLocation = null;

        glyphStartLocation = null;
        glyphEndLocation = null;

        selectedCount = 0;
    }

    public void clearSelectionState() {
        java.util.List pages = documentViewModel.getPageComponents();
        for (AbstractPageViewComponent page : pages) {
            ((PageViewComponentImpl)page).getTextSelectionPageHandler().clearSelection();
        }
    }

    public void selection(Point dragPoint, AbstractPageViewComponent pageViewComponent,
                          boolean isDown, boolean isMovingRight) {
        try {
            if (pageViewComponent != null) {

                // acquire a page lock.
                pageLock = pageViewComponent.getPage();

                boolean isLocalDown;
                if (lastMouseLocation != null) {
                    // double check we're actually moving down
                    isLocalDown = lastMouseLocation.y <= dragPoint.y;
                } else {
                    isLocalDown = isDown;
                }
                multiLineSelectHandler(pageViewComponent, dragPoint, isDown, isLocalDown, isMovingRight);

                lastMouseLocation = dragPoint;
            }
        } catch (InterruptedException e) {
            logger.fine("Text selection page access interrupted");
        }
    }

    public void selectionIcon(Point mouseLocation, AbstractPageViewComponent pageViewComponent) {
        try {
            Page currentPage = pageViewComponent.getPage();
            if (currentPage != null) {
                // get page text
                PageText pageText = currentPage.getViewText();
                if (pageText != null) {

                    // get page transform, same for all calculations
                    AffineTransform pageTransform = currentPage.getPageTransform(
                            Page.BOUNDARY_CROPBOX,
                            documentViewModel.getViewRotation(),
                            documentViewModel.getViewZoom());

                    // create exclusion boxes
                    calculateTextSelectionExclusion(pageTransform);

                    ArrayList pageLines = pageText.getPageLines();
                    if (pageLines != null) {
                        boolean found = false;
                        Point2D.Float pageMouseLocation =
                                convertMouseToPageSpace(mouseLocation, pageTransform);

                        for (LineText pageLine : pageLines) {
                            // check for containment, if so break into words.
                            if (pageLine.getBounds().contains(pageMouseLocation)
                                    && ((topMarginExclusion == null || bottomMarginExclusion == null)
                                    || (!topMarginExclusion.contains(pageMouseLocation)
                                    && !bottomMarginExclusion.contains(pageMouseLocation)))) {
                                found = true;
                                documentViewController.setViewCursor(
                                        DocumentViewController.CURSOR_TEXT_SELECTION);
                                break;
                            }
                        }
                        if (!found) {
                            documentViewController.setViewCursor(
                                    DocumentViewController.CURSOR_SELECT);
                        }
                    }
                }
            }
        } catch (InterruptedException e) {
            logger.fine("Text selection page access interrupted");
        }
    }

    protected void calculateTextSelectionExclusion(AffineTransform pageTransform) {
        if (enableMarginExclusion) {
            Rectangle2D mediaBox = pageViewComponent.getPage().getCropBox();
            topMarginExclusion = new Rectangle2D.Float(0, (int) mediaBox.getHeight() - topMargin,
                    (int) mediaBox.getWidth(), topMargin);
            bottomMarginExclusion = new Rectangle2D.Float(0, 0,
                    (int) mediaBox.getWidth(), bottomMargin);
        }
    }

    /**
     * Paints any text that is selected in the page wrapped by a pageViewComponent.
     *
     * @param g                 graphics context to paint to.
     * @param pageViewComponent page view component to paint selected to on.
     * @param documentViewModel document model contains view properties such as zoom and rotation.
     */
    public static void paintSelectedText(Graphics g,
                                         AbstractPageViewComponent pageViewComponent,
                                         DocumentViewModel documentViewModel) throws InterruptedException{
        // ready outline paint
        Graphics2D gg = (Graphics2D) g;
        AffineTransform prePaintTransform = gg.getTransform();
        Color oldColor = gg.getColor();
        Stroke oldStroke = gg.getStroke();
//        gg.setComposite(BlendComposite.getInstance(BlendComposite.BlendingMode.MULTIPLY, 1.0f));
        gg.setComposite(AlphaComposite.getInstance(
                AlphaComposite.SRC_OVER,
                Page.SELECTION_ALPHA));
        gg.setColor(Page.selectionColor);
        gg.setStroke(new BasicStroke(1.0f));

        Page currentPage = pageViewComponent.getPage();
        if (currentPage != null) {
            PageText pageText = currentPage.getViewText();
            if (pageText != null) {
                // get page transformation
                AffineTransform pageTransform = currentPage.getPageTransform(
                        documentViewModel.getPageBoundary(),
                        documentViewModel.getViewRotation(),
                        documentViewModel.getViewZoom());
                // paint the sprites
                GeneralPath textPath;
                ArrayList visiblePageLines = pageText.getPageLines();
                if (visiblePageLines != null) {
                    for (LineText lineText : visiblePageLines) {
                        for (WordText wordText : lineText.getWords()) {
                            // paint whole word
                            if (wordText.isSelected() || wordText.isHighlighted()) {
                                textPath = new GeneralPath(wordText.getBounds());
                                textPath.transform(pageTransform);
                                // paint highlight over any selected
                                if (wordText.isSelected()) {
                                    gg.setColor(Page.selectionColor);
                                    gg.fill(textPath);
                                }
                                if (wordText.isHighlighted()) {
                                    gg.setColor(Page.highlightColor);
                                    gg.fill(textPath);
                                }
                            }
                            // check children
                            else {
                                for (GlyphText glyph : wordText.getGlyphs()) {
                                    if (glyph.isSelected()) {
                                        textPath = new GeneralPath(glyph.getBounds());
                                        textPath.transform(pageTransform);
                                        gg.setColor(Page.selectionColor);
                                        gg.fill(textPath);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
//        gg.setComposite(BlendComposite.getInstance(BlendComposite.BlendingMode.NORMAL, 1.0f));
        // restore graphics state to where we left it.
        gg.setTransform(prePaintTransform);
        gg.setStroke(oldStroke);
        gg.setColor(oldColor);

        // paint words for bounds test.
//        paintTextBounds(g);

    }

    /**
     * Utility for painting text bounds.
     *
     * @param g graphics context to paint to.
     */
    protected void paintTextBounds(Graphics g) throws InterruptedException{
        Page currentPage = pageViewComponent.getPage();
        // get page transformation
        AffineTransform pageTransform = currentPage.getPageTransform(
                documentViewModel.getPageBoundary(),
                documentViewModel.getViewRotation(),
                documentViewModel.getViewZoom());
        Graphics2D gg = (Graphics2D) g;
        Color oldColor = g.getColor();
        g.setColor(Color.red);

        PageText pageText = currentPage.getViewText();
        if (pageText != null) {
            ArrayList pageLines = pageText.getPageLines();
            if (pageLines != null) {
                for (LineText lineText : pageLines) {

                    for (WordText wordText : lineText.getWords()) {
                        for (GlyphText glyph : wordText.getGlyphs()) {
                            g.setColor(Color.black);
                            GeneralPath glyphSpritePath =
                                    new GeneralPath(glyph.getBounds());
                            glyphSpritePath.transform(pageTransform);
                            gg.draw(glyphSpritePath);
                        }

                        //                if (!wordText.isWhiteSpace()) {
                        //                    g.setColor(Color.blue);
                        //                    GeneralPath glyphSpritePath =
                        //                            new GeneralPath(wordText.getBounds());
                        //                    glyphSpritePath.transform(pageTransform);
                        //                    gg.draw(glyphSpritePath);
                        //                }
                    }
                    g.setColor(Color.red);
                    GeneralPath glyphSpritePath =
                            new GeneralPath(lineText.getBounds());
                    glyphSpritePath.transform(pageTransform);
                    gg.draw(glyphSpritePath);
                }
            }
        }
        g.setColor(oldColor);
    }

    /**
     * Entry point for multiline text selection.  Contains logic for moving from once page to the next which boils
     * down to defining a start position when a new page is entered.
     *
     * @param pageViewComponent page view that is being acted.
     * @param mouseLocation     current mouse location already normalized to page space. .
     * @param isDown            general selection trent is down, if false it's up.
     * @param isMovingRight     general selection trent is right, if alse it's left.
     */
    protected void multiLineSelectHandler(AbstractPageViewComponent pageViewComponent, Point mouseLocation,
                                          boolean isDown, boolean isLocalDown, boolean isMovingRight) throws InterruptedException{
        Page currentPage = pageViewComponent.getPage();
        selectedCount = 0;

        if (currentPage != null) {
            // get page text
            PageText pageText = currentPage.getViewText();
            if (pageText != null) {

                // clear the currently selected state, ignore highlighted.
                pageText.clearSelected();

                // get page transform, same for all calculations
                AffineTransform pageTransform = currentPage.getPageTransform(
                        documentViewModel.getPageBoundary(),
                        documentViewModel.getViewRotation(),
                        documentViewModel.getViewZoom());

                ArrayList pageLines = pageText.getPageLines();

                // create exclusion boxes
                calculateTextSelectionExclusion(pageTransform);

                // normalize the mouse coordinates to page space
                Point2D.Float draggingMouseLocation = convertMouseToPageSpace(mouseLocation, pageTransform);

                // dragging mouse into a page or from white space, neither glyphStart or glyphEnd will be initialized.
                if (glyphStartLocation == null) {
                    glyphStartLocation = GlyphLocation.findFirstGlyphLocation(pageLines, draggingMouseLocation, isDown, isLocalDown,
                            lastGlyphEndLocation, topMarginExclusion, bottomMarginExclusion);
                    // if we're close to something mark the isFirst=false.
                    if (glyphStartLocation != null) {
                        glyphEndLocation = new GlyphLocation(glyphStartLocation);
                        isFirst = false;
                    }
                } else if (glyphStartLocation != null) {
                    // should already have start but no end.
                    glyphEndLocation = GlyphLocation.findGlyphLocation(pageLines, draggingMouseLocation, isDown, isLocalDown,
                            lastGlyphEndLocation, topMarginExclusion, bottomMarginExclusion);
                }

                // normal page selection,  fill in the the highlight between start and end.
                if (glyphStartLocation != null && glyphEndLocation != null) {
                    selectedCount = GlyphLocation.highLightGlyphs(pageLines, glyphStartLocation, glyphEndLocation, leftToRight,
                            isDown, isLocalDown, isMovingRight, topMarginExclusion, bottomMarginExclusion);
                    lastGlyphStartLocation = glyphStartLocation;
                    lastGlyphEndLocation = glyphEndLocation;
                }
                // check if last draw are still around and draw them.
                else if (lastGlyphStartLocation != null && lastGlyphEndLocation != null) {
                    selectedCount = GlyphLocation.highLightGlyphs(pageLines, lastGlyphStartLocation, lastGlyphEndLocation, leftToRight,
                            isDown, isLocalDown, isMovingRight, topMarginExclusion, bottomMarginExclusion);
                }
            }
            pageViewComponent.repaint();
        }
    }

    /**
     * Utility for selecting multiple lines via rectangle like tool. The
     * selection works based on the intersection of the rectangle and glyph
     * bounding box.
     * 

* This method should only be called from within a locked page content * * @param currentPage page to looking for text intersection on. * @param mouseLocation location of mouse. */ protected void wordSelectHandler(Page currentPage, Point mouseLocation) throws InterruptedException { if (currentPage != null) { // get page text PageText pageText = currentPage.getViewText(); if (pageText != null) { // clear the currently selected state, ignore highlighted. pageText.clearSelected(); // get page transform, same for all calculations AffineTransform pageTransform = currentPage.getPageTransform( Page.BOUNDARY_CROPBOX, documentViewModel.getViewRotation(), documentViewModel.getViewZoom()); Point2D.Float pageMouseLocation = convertMouseToPageSpace(mouseLocation, pageTransform); ArrayList pageLines = pageText.getPageLines(); if (pageLines != null) { for (LineText pageLine : pageLines) { // check for containment, if so break into words. if (pageLine.getBounds().contains(pageMouseLocation)) { pageLine.setHasSelected(true); java.util.List lineWords = pageLine.getWords(); for (WordText word : lineWords) { // if (word.contains(pageTransform, mouseLocation)) { if (word.getBounds().contains(pageMouseLocation)) { word.selectAll(); // let the ri know we have selected text. documentViewModel.addSelectedPageText(pageViewComponent); documentViewController.firePropertyChange( PropertyConstants.TEXT_SELECTED, null, null); pageViewComponent.repaint(); break; } } } } } } } } /** * Utility for selecting a LineText which is usually a sentence in the * document. This is usually triggered by a triple click of the mouse * * @param currentPage page to select * @param mouseLocation location of mouse */ protected void lineSelectHandler(Page currentPage, Point mouseLocation) throws InterruptedException { if (currentPage != null) { // get page text PageText pageText = currentPage.getViewText(); if (pageText != null) { // clear the currently selected state, ignore highlighted. pageText.clearSelected(); // get page transform, same for all calculations AffineTransform pageTransform = currentPage.getPageTransform( Page.BOUNDARY_CROPBOX, documentViewModel.getViewRotation(), documentViewModel.getViewZoom()); Point2D.Float pageMouseLocation = convertMouseToPageSpace(mouseLocation, pageTransform); ArrayList pageLines = pageText.getPageLines(); if (pageLines != null) { for (LineText pageLine : pageLines) { // check for containment, if so break into words. if (pageLine.getBounds().contains(pageMouseLocation)) { pageLine.selectAll(); // let the ri know we have selected text. documentViewModel.addSelectedPageText(pageViewComponent); documentViewController.firePropertyChange( PropertyConstants.TEXT_SELECTED, null, null); pageViewComponent.repaint(); break; } } } } } } @Override public void setSelectionRectangle(Point cursorLocation, Rectangle selection) { } /** * Sets the top margin used to define an exclusion zone for text selection. For this value * to be applied the system property -Dorg.icepdf.core.views.page.marginExclusion.enabled=true * must be set. * * @param topMargin top margin height in pixels. */ public void setTopMargin(int topMargin) { this.topMargin = topMargin; } /** * Sets the bottom margin used to define an exclusion zone for text selection. For this value * to be applied the system property -Dorg.icepdf.core.views.page.marginExclusion.enabled=true * must be set. * * @param bottomMargin bottom margin height in pixels. */ public void setBottomMargin(int bottomMargin) { this.bottomMargin = bottomMargin; } } class GlyphLocation { private int line, word, glyph; public GlyphLocation(int line, int word, int glyph) { this.line = line; this.word = word; this.glyph = glyph; } public GlyphLocation(GlyphLocation glyphLocation) { this.line = glyphLocation.line; this.word = glyphLocation.word; this.glyph = glyphLocation.glyph; } @Override public String toString() { return "GlyphLocation{" + "line=" + line + ", word=" + word + ", glyph=" + glyph + '}'; } public static WordText getWord(ArrayList pageLines, GlyphLocation location) { return pageLines.get(location.line).getWords().get(location.word); } public static GlyphText getGlyph(ArrayList pageLines, GlyphLocation location) { return pageLines.get(location.line).getWords().get(location.word).getGlyphs().get(location.glyph); } public static GlyphLocation multiPageSelectGlyphLocation(ArrayList pageLines, Point2D.Float mouseLocation, boolean isDown, boolean leftToRight, Shape topMarginExclusion, Shape bottomMarginExclusion) { if (pageLines == null) return null; // find first glyph of first line if (isDown && leftToRight) { for (int i = 0, max = pageLines.size(); i < max; i++) { if (isLineTextIncluded(pageLines.get(i), topMarginExclusion, bottomMarginExclusion) && findMouseContainedWithInLine(mouseLocation, pageLines.get(i))) { return new GlyphLocation(i, 0, 0); } } } // first line and last glyph else if (isDown) { for (int i = 0, max = pageLines.size(); i < max; i++) { if (isLineTextIncluded(pageLines.get(i), topMarginExclusion, bottomMarginExclusion) && findMouseContainedWithInLine(mouseLocation, pageLines.get(i))) { int lastWordIndex = pageLines.get(i).getWords().size() - 1; WordText lastWord = pageLines.get(i).getWords().get(lastWordIndex); return new GlyphLocation(i, lastWordIndex, lastWord.getGlyphs().size() - 1); } } } // going up is always right to left. else { for (int i = pageLines.size() - 1; i >= 0; i--) { if (isLineTextIncluded(pageLines.get(i), topMarginExclusion, bottomMarginExclusion) && findMouseContainedWithInLine(mouseLocation, pageLines.get(i))) { int lastWordIndex = pageLines.get(i).getWords().size() - 1; WordText lastWord = pageLines.get(i).getWords().get(lastWordIndex); return new GlyphLocation(i, lastWordIndex, lastWord.getGlyphs().size() - 1); } } } return null; } public static boolean isLineTextIncluded(LineText lineText, Shape topMarginExclusion, Shape bottomMarginExclusion) { if (enableMarginExclusion) { return !(topMarginExclusion.contains(lineText.getBounds()) || bottomMarginExclusion.contains(lineText.getBounds())); } return true; } public static GlyphLocation findGlyphLocation(ArrayList pageLines, Point2D.Float cursorLocation, boolean isDown, boolean isLocalDown, GlyphLocation lastGlyphEndLocation, Shape topMarginExclusion, Shape bottomMarginExclusion) { if (pageLines != null) { // check for a direct intersection. GlyphLocation glyphLocation = findGlyphIntersection(pageLines, cursorLocation, topMarginExclusion, bottomMarginExclusion); if (glyphLocation != null) return glyphLocation; // check mouse location against y-coordinate of a line and grab the last line // this is buggy if the lines aren't sorted via !org.icepdf.core.views.page.text.preserveColumns. if ((isLocalDown && isDown) || isLocalDown) { int lastGlyphEndLine = 0; if (lastGlyphEndLocation != null) { lastGlyphEndLine = lastGlyphEndLocation.line; } // get the next line last word. int lineIndex = lastGlyphEndLine; for (int lineMax = pageLines.size(); lineIndex < lineMax - 1; lineIndex++) { float y1 = pageLines.get(lineIndex).getBounds().y; float y2 = pageLines.get(lineIndex + 1).getBounds().y; if (cursorLocation.y < y1 && cursorLocation.y >= y2) { LineText lineText = pageLines.get(lineIndex + 1); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { java.util.List words = lineText.getWords(); return new GlyphLocation(lineIndex + 1, words.size() - 1, words.get(words.size() - 1).getGlyphs().size() - 1); } } } // else fill the line if (lastGlyphEndLine < pageLines.size() && lastGlyphEndLine > pageLines.size() - 5) { LineText lineText = pageLines.get(lastGlyphEndLine); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { java.util.List words = lineText.getWords(); return new GlyphLocation(lastGlyphEndLine, words.size() - 1, words.get(words.size() - 1).getGlyphs().size() - 1); } } } else { if (lastGlyphEndLocation != null) { // find left most world. int lineIndex = lastGlyphEndLocation.line; for (; lineIndex > 0; lineIndex--) { float y1 = pageLines.get(lineIndex).getBounds().y; float y2 = pageLines.get(lineIndex - 1).getBounds().y; if (cursorLocation.y >= y1 && cursorLocation.y < y2) { LineText lineText = pageLines.get(lineIndex - 1); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { return new GlyphLocation(lineIndex - 1, 0, 0); } } } // else fill the line // todo setup loop to find line closed to the exclusion, not just the first line. if (lastGlyphEndLocation.line < 5) { LineText lineText = pageLines.get(lastGlyphEndLocation.line); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { return new GlyphLocation(lastGlyphEndLocation.line, 0, 0); } } } } } return null; } public static GlyphLocation findGlyphIntersection(ArrayList pageLines, Point2D.Float cursorLocation, Shape topMarginExclusion, Shape bottomMarginExclusion) { LineText pageLine; // check for a direct intersection. for (int lineIndex = 0, lineMax = pageLines.size(); lineIndex < lineMax; lineIndex++) { pageLine = pageLines.get(lineIndex); if (pageLine.intersects(cursorLocation) && isLineTextIncluded(pageLine, topMarginExclusion, bottomMarginExclusion)) { java.util.List lineWords = pageLines.get(lineIndex).getWords(); WordText currentWord; for (int wordIndex = 0, wordMax = lineWords.size(); wordIndex < wordMax; wordIndex++) { currentWord = lineWords.get(wordIndex); if (currentWord.intersects(cursorLocation)) { ArrayList glyphs = currentWord.getGlyphs(); for (int glyphIndex = 0, glyphMax = glyphs.size(); glyphIndex < glyphMax; glyphIndex++) { GlyphText currentGlyph = glyphs.get(glyphIndex); if (currentGlyph.intersects(cursorLocation)) { return new GlyphLocation(lineIndex, wordIndex, glyphIndex); } } } } } } return null; } public static GlyphLocation findFirstGlyphLocation(ArrayList pageLines, Point2D.Float cursorLocation, boolean isDown, boolean isLocalDown, GlyphLocation lastGlyphEndLocation, Shape topMarginExclusion, Shape bottomMarginExclusion) { if (pageLines != null) { // check mouse location against y-coordinate of a line and depending on direction pick // first or last world a line. if (isDown) { // find first word of first y line int lineIndex = 0; for (int lineMax = pageLines.size() - 1; lineIndex < lineMax; lineIndex++) { float y1 = pageLines.get(lineIndex).getBounds().y; float y2 = pageLines.get(lineIndex + 1).getBounds().y; if (cursorLocation.y < (y1 + pageLines.get(lineIndex).getBounds().height) && cursorLocation.y >= (y2 + pageLines.get(lineIndex + 1).getBounds().height)) { LineText lineText = pageLines.get(lineIndex); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { return new GlyphLocation(lineIndex, 0, 0); } } } } // going up so check against y bottom up and then pick last work. else { // find left most world. int lineIndex = pageLines.size() - 1; for (; lineIndex > 0; lineIndex--) { float y1 = pageLines.get(lineIndex - 1).getBounds().y; float y2 = pageLines.get(lineIndex).getBounds().y; if (cursorLocation.y < (y1 + pageLines.get(lineIndex - 1).getBounds().height) && cursorLocation.y >= (y2 + pageLines.get(lineIndex).getBounds().height)) { LineText lineText = pageLines.get(lineIndex); if (isLineTextIncluded(lineText, topMarginExclusion, bottomMarginExclusion)) { java.util.List words = lineText.getWords(); return new GlyphLocation(lineIndex, words.size() - 1, words.get(words.size() - 1).getGlyphs().size() - 1); } } } } } return null; } public static boolean findMouseContainedWithInLine(Point2D.Float mouseLocation, LineText pageLines) { return pageLines.getBounds().contains(mouseLocation); } public static int highLightGlyphs(ArrayList pageLines, GlyphLocation start, GlyphLocation end, boolean leftToRight, boolean isDown, boolean isLocalDown, boolean isRight, Shape topMarginExclusion, Shape bottomMarginExclusion) { if (pageLines == null) return 0; int selectedCount = fillFirstLine(pageLines.get(start.line), start, end, isDown, isRight, leftToRight); // fill middle, if any selectedCount += fillMiddleLines(pageLines, start, end, topMarginExclusion, bottomMarginExclusion); // fill last line, last line if any selectedCount += fillLastLine(pageLines.get(end.line), start, end, isDown, isRight, leftToRight); return selectedCount; } public static int fillFirstLine(LineText pageLine, GlyphLocation start, GlyphLocation end, boolean isDown, boolean isRight, boolean isLTR) { pageLine.setHasHighlight(true); java.util.List lineWords = pageLine.getWords(); int selectedCount = 0; // last half of the first word selectedCount += fillFirstWord(lineWords, start, end, isRight, isDown); // first half of the last word selectedCount += fillLastWord(lineWords, start, end, isRight, isDown); if (start.line == end.line) { if (isRight && end.word > start.word) { // fill left to right for (int wordIndex = start.word + 1; wordIndex <= end.word - 1; wordIndex++) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } else { // fill right to left for (int wordIndex = start.word - 1; wordIndex >= end.word + 1; wordIndex--) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } } else if ((isRight && isDown) || (!isRight && isDown)) { // fill right to end of line for (int wordIndex = start.word + 1; wordIndex < lineWords.size(); wordIndex++) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } else {// if ((isRight && !isDown) || (!isRight && !isDown)){ // fill left to start of line for (int wordIndex = start.word - 1; wordIndex >= 0; wordIndex--) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } return selectedCount; } public static int fillFirstWord(java.util.List words, GlyphLocation start, GlyphLocation end, boolean isRight, boolean isDown) { int selectedCount = 0; if (end != null && start.line == end.line) { if (start.word == end.word) { if (isRight) { // same word so we move to select start->end. WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex <= end.glyph; glyphIndex++) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } else { WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex >= end.glyph; glyphIndex--) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } } else { if (isRight && end.word > start.word) { WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex < word.getGlyphs().size(); glyphIndex++) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } else { WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex >= 0; glyphIndex--) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } } } else if ((isRight && isDown) || (!isRight && isDown)) { WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex < word.getGlyphs().size(); glyphIndex++) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } else {//if ((isRight && !isDown) || (!isRight && !isDown)) { WordText word = words.get(start.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex >= 0; glyphIndex--) { word.getGlyphs().get(glyphIndex).setSelected(true); } } return selectedCount; } public static int fillLastWord(java.util.List words, GlyphLocation start, GlyphLocation end, boolean isRight, boolean isDown) { int selectedCount = 0; if (isRight && end.word > start.word) { // same word so we move to select start->end. if (start.word == end.word && start.line == end.line) { // nothing to do handled by the first word. } // at least two words so we can do the last half of start and first half of end. else if (start.line == end.line) { WordText word = words.get(end.word); word.setHasSelected(true); for (int glyphIndex = 0; glyphIndex <= end.glyph; glyphIndex++) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } } else { // same word so we move to select start->end. if (start.word == end.word && start.line == end.line) { WordText word = words.get(end.word); word.setHasSelected(true); for (int glyphIndex = start.glyph; glyphIndex >= end.glyph; glyphIndex--) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } // at least two words so we can do the last half of start and first half of end. else if (start.line == end.line) { WordText word = words.get(end.word); word.setHasSelected(true); for (int glyphIndex = word.getGlyphs().size() - 1; glyphIndex >= end.glyph; glyphIndex--) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } } } return selectedCount; } public static int fillLastLine(LineText pageLine, GlyphLocation start, GlyphLocation end, boolean isDown, boolean isRight, boolean isLTR) { java.util.List lineWords = pageLine.getWords(); int selectedCount = 0; if (start.line != end.line) { pageLine.setHasHighlight(true); if (isDown) { WordText word = lineWords.get(end.word); word.setHasSelected(true); for (int glyphIndex = 0; glyphIndex <= end.glyph; glyphIndex++) { word.getGlyphs().get(glyphIndex).setSelected(true); selectedCount++; } for (int wordIndex = 0; wordIndex < end.word; wordIndex++) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } else { selectedCount += fillFirstWord(lineWords, end, null, isRight, true); // for (int wordIndex = end.word + 1; wordIndex < lineWords.size(); wordIndex++) { lineWords.get(wordIndex).selectAll(); selectedCount++; } } } return selectedCount; } public static int fillMiddleLines(ArrayList pageLines, GlyphLocation start, GlyphLocation end, Shape topMarginExclusion, Shape bottomMarginExclusion) { GlyphLocation startLocal = new GlyphLocation(start); GlyphLocation endLocal = new GlyphLocation(end); if (startLocal.line > endLocal.line) { GlyphLocation tmp = startLocal; startLocal = end; endLocal = tmp; } int selectedCount = 0; for (int lineIndex = startLocal.line + 1; lineIndex < endLocal.line; lineIndex++) { if (isLineTextIncluded(pageLines.get(lineIndex), topMarginExclusion, bottomMarginExclusion)) { pageLines.get(lineIndex).selectAll(); selectedCount++; } } return selectedCount; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy