Ticket #2422: text_justification.2.patch

File text_justification.2.patch, 12.4 KB (added by boyanl, 15 years ago)
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/mvc/TextModelLogic.java

    ### Eclipse Workspace Patch 1.0
    #P sophie
     
    107107                                if (clickCount % 3 == 2) { 
    108108                                        Break wordBreak = Breaks.WORD_BREAK; 
    109109                                        int endPos =  wordBreak.getNextBreakPos(rawText, pos); 
    110                                         while (endPos < rawText.getEnd()  
     110                                        while (endPos <= rawText.getEnd()  
    111111                                                        && endPos > rawText.getBegin() 
    112112                                                        && CommonChar.getWordSeparators(). 
    113113                                                        contains(rawText.unitAt(endPos - 1).getChar())) { 
     
    745745                case LINE_END : 
    746746                        lineEnds = layout.getLineEnds(caret); 
    747747                        if (lineEnds != null) { 
    748                                 result = ImmTextUtils.advance(text, textBegin, lineEnds.getEnd()); 
     748                                int end = lineEnds.getEnd(); 
     749                                while (end > text.getBegin() && end < text.getEnd() && text.unitAt(end).getChar() == CommonChar.PARA_BREAK) { 
     750                                        end = ImmTextUtils.advance(text, end, -1); 
     751                                } 
     752                                         
     753                                result = ImmTextUtils.advance(text, textBegin, end); 
    749754                        } 
    750755                        break; 
    751756 
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/model/TextUtils.java

     
    2626         */ 
    2727        public static int findPrevChar(ImmText text, int startPos, List<Character> chars) { 
    2828                int pos = (startPos != -1) ? startPos : text.getBegin(); 
     29                if (pos != text.getBegin() && pos == text.getEnd()) { 
     30                        --pos; 
     31                } 
    2932 
    3033                while (pos > text.getBegin()) { 
    3134                        if (chars.contains(text.unitAt(pos).getChar())) { 
     
    5659         */ 
    5760        public static int findNextChar(ImmText text, int startIndex, List<Character> chars) { 
    5861 
    59                 assert startIndex >= 0 && startIndex < text.getEnd() : startIndex; 
     62                assert startIndex >= 0 && startIndex <= text.getEnd() : startIndex; 
     63                if (startIndex == text.getEnd ()) 
     64                        return startIndex; 
    6065                int index = startIndex + 1; 
    6166                 
    6267                while (index < text.getEnd()) { 
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/layout/LayoutUtils.java

     
    5858 
    5959                        List<Character> lineBreaks = CommonChar.getEffectiveBreaks(CommonChar.LINE_BREAK); 
    6060                        int pos = TextUtils.findNextChar(this.areaText, begin, lineBreaks); 
    61  
     61                         
    6262                        if (pos == -1 || this.areaText.getEnd() == 0) {  
    6363                                return null; 
    6464                        } 
     
    7070                                        pos = nextPos; 
    7171                                } 
    7272                        } 
     73                         
     74                        //checks for trailing para break and adds it (if any) 
     75                        int paraBreak = nextPos; 
     76                        if (paraBreak < this.areaText.getEnd() &&  
     77                                        this.areaText.unitAt(paraBreak).getChar() == CommonChar.PARA_BREAK ) 
     78                                ++paraBreak; 
    7379 
    7480                        assert ImmTextUtils.isIndexInText(pos, this.areaText); 
    7581                        ImmText res =  
    7682                                this.areaText.subText(new ImmTextInterval(begin,  
    77                                                 Math.min(nextPos, this.areaText.getEnd()))); 
     83                                                Math.min(paraBreak, this.areaText.getEnd()))); 
    7884                        this.areaText =  
    79                                 this.areaText.subText(new ImmTextInterval(pos, end)); 
     85                                this.areaText.subText(new ImmTextInterval(paraBreak, end)); 
    8086 
    8187                        // Empty texts should not be returned. 
    8288                        if (res.getEnd() == 0) { 
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/layout/LineBreakUtil.java

     
    55 
    66import org.sophie2.base.model.text.elements.Break; 
    77import org.sophie2.base.model.text.elements.Breaks; 
     8import org.sophie2.base.model.text.elements.CommonChar; 
    89import org.sophie2.base.model.text.model.ImmText; 
    910import org.sophie2.base.model.text.model.TextRun; 
    1011 
     
    124125                 *                      The text to take a word from. 
    125126                 * @param startIndex 
    126127                 *                      The index from which to search the word end. 
     128                 * @param whitespaceBreaks  
     129                 *                      An list of indices containing all the positions of characters who come after consecutive whitespaces 
    127130                 * @return 
    128131                 *                      A {@link Word} with starting index - startIndex and ending index - the first  
    129132                 *                      non-word-breaking character index after the first sequence of white spaces. 
    130133                 */ 
    131                 static Word get(ImmText text, int startIndex) { 
     134                static Word get(ImmText text, int startIndex, List<Integer> whitespaceBreaks) { 
    132135 
    133136                        int requiredEnd = getWordEnd(text, startIndex); 
    134137 
     
    138141 
    139142                        assert requiredEnd <= text.getEnd(); 
    140143 
     144                        if (whitespaceBreaks != null) { 
     145                                /* 
     146                                 * Construct the list of whitespace positions. The actual values in the list are the first characters *after* 
     147                                 * whitespaces. Also if the word has trailing whitespaces, the position after the word's end is inserted in the list. 
     148                                 */ 
     149                                int ind = start; 
     150                                while (ind < requiredEnd) { 
     151                                        char c = text.unitAt(ind).getChar(); 
     152                                        if (c == CommonChar.SPACE || 
     153                                                c == CommonChar.TAB){ 
     154                                                 
     155                                                while (ind < requiredEnd &&  
     156                                                                (c == CommonChar.SPACE || 
     157                                                                c == CommonChar.TAB || 
     158                                                                c == CommonChar.LINE_BREAK)) { 
     159                                                        c = text.unitAt(ind++).getChar(); 
     160                                                } 
     161                                                         
     162                                                whitespaceBreaks.add(ind); 
     163                                        } 
     164                                        else 
     165                                                ++ind; 
     166                                } 
     167                                 
     168                                /* 
     169                                 * Construct each run between whitespace positions, starting from start 
     170                                 */ 
     171                                for (int position : whitespaceBreaks) { 
     172                                        while (start < position  
     173                                                        && (run = TextRun.create(text, start, position)) != null) { 
     174                                                runs.add (run); 
     175                                                start += run.getRunLength(); 
     176                                        } 
     177                                        start = position; 
     178                                } 
     179                        } 
     180                         
     181                        //Construct the final run (from the last whitespace position till the end of the word) 
    141182                        while (start < requiredEnd  
    142183                                        && (run = TextRun.create(text, start, requiredEnd)) != null) { 
    143184                                runs.add(run); 
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/elements/Breaks.java

     
    3636                                } 
    3737                                tmpPos += advanceStep; 
    3838                        } 
    39                         return position; 
     39                        return tmpPos; 
    4040                } 
    4141        }; 
    4242 
  • modules/org.sophie2.base.model.text/src/main/java/org/sophie2/base/model/text/layout/HotSegmentLayout.java

     
    99import java.awt.geom.Line2D; 
    1010import java.awt.geom.Rectangle2D; 
    1111import java.util.ArrayList; 
     12import java.util.Arrays; 
    1213 
    1314import org.sophie2.base.commons.util.position.ImmPoint; 
    1415import org.sophie2.base.model.text.elements.CommonAttr; 
     
    3334         * Layout with no text. Used to fill spaces that should stay empty. 
    3435         */ 
    3536        static final HotSegmentLayout EMPTY =  
    36                 new HotSegmentLayout(new ArrayList<TextRun>(), new ArrayList<DoublePoint>(), 0); 
     37                new HotSegmentLayout(new ArrayList<TextRun>(), new ArrayList<DoublePoint>(), new int[0], 0, 0); 
    3738 
    3839        private final ArrayList<TextRun> textRuns; 
    3940        private final ArrayList<DoublePoint> locations; 
     41        private final int[] wordPos; 
    4042        private final double xOffset; 
     43        private final double textWidth; 
    4144 
    4245        private HotSegmentLayout( 
    43                         ArrayList<TextRun> runs, ArrayList<DoublePoint> locations, double xOffset) { 
     46                        ArrayList<TextRun> runs, ArrayList<DoublePoint> locations, int[] wordPos, double xOffset, double textWidth) { 
    4447                 
    4548                this.textRuns = runs; 
    4649                this.locations = locations; 
    4750                this.xOffset = xOffset; 
     51                this.textWidth = textWidth; 
     52                this.wordPos = wordPos; 
    4853        } 
    4954 
    5055        /** 
     
    6873                double remainingWidth = totalWidth; 
    6974                ArrayList<TextRun> runs = new ArrayList<TextRun>(); 
    7075                ArrayList<DoublePoint> locations = new ArrayList<DoublePoint>(); 
     76                 
     77                //contains the start indices of whitespace-delimited words (except the first one)  
     78                ArrayList<Integer> wordPos = new ArrayList<Integer> (); 
    7179 
    7280                int position = startPos; 
    73                 Word word; 
     81                Word word = null; 
    7482 
    7583                // Add as many words as possible. 
     84                ArrayList<Integer> curWordPos = new ArrayList<Integer> (); 
    7685                while (position < text.getEnd() &&  
    77                                 (word = Word.get(text, position)) != null && 
     86                                (word = Word.get(text, position, curWordPos)) != null && 
    7887                                word.willFit(totalWidth, remainingWidth) && remainingWidth > 0) { 
    7988 
    8089                        for (TextRun run : word.getRuns()) { 
     
    98107                        } 
    99108 
    100109                        position += word.getSize(); 
     110                        wordPos.addAll(curWordPos); 
     111                        curWordPos.clear(); 
    101112                } 
    102  
     113                 
     114                /* 
     115                 * Position contains the first position of a word we were unable to add - either the end of the text (i.e. we added everything), or 
     116                 * some index before it. If we have it in wordPos (i.e. it was the first positions after whitespaces), we have to remove it. 
     117                 */ 
     118                if (!wordPos.isEmpty () && wordPos.get(wordPos.size() - 1) == position) { 
     119                        wordPos.remove(wordPos.size() - 1); 
     120                } 
     121         
    103122                // If no words are added, add as many chars as possible. 
    104123                if (runs.size() == 0) { 
    105124                        SophieLog.debug("Could not layout a single word! Will split it to chars!"); 
     
    113132                                TextRun run = character.getRun(); 
    114133 
    115134                                remainingWidth = addRun(run, baselineOffset,  
    116                                                 totalWidth, remainingWidth, runs, locations); 
     135                                                                        totalWidth, remainingWidth, runs, locations); 
    117136 
    118137                                if (remainingWidth == 0) { 
    119138                                        break; 
     
    143162 
    144163                TextAlign align = runs.get(0).getAttrValue(CommonAttr.PARA_ALIGNMENT); 
    145164 
     165                int[] arr = new int[wordPos.size()]; 
     166                for (int i = 0; i < wordPos.size(); ++i) 
     167                        arr[i] = wordPos.get(i) - startPos; 
     168                 
    146169                HotSegmentLayout result =  
    147                         new HotSegmentLayout(runs, locations, getAlignOffset(align, remainingWidth)); 
     170                        new HotSegmentLayout(runs, locations, arr, getAlignOffset(align, remainingWidth), totalWidth - remainingWidth); 
    148171 
    149                 if (TextAlign.JUSTIFIED.equals(align)) { 
     172                //don't justify if the segments end with para/doc break 
     173                char lastChar = text.unitAt(startPos + result.getConsumedLength() - 1).getChar (); 
     174                boolean shouldJustify = lastChar != CommonChar.PARA_BREAK && lastChar != CommonChar.DOC_BREAK; 
     175                if (TextAlign.JUSTIFIED.equals(align) && shouldJustify) { 
    150176                        return result.getJustifiedLayout(totalWidth); 
    151177                } 
    152178 
     
    391417        } 
    392418 
    393419        private HotSegmentLayout getJustifiedLayout(double totalWidth) { 
    394                 // TODO don't know how to do this.. --kyli 
    395                 return this; 
     420                HotSegmentLayout result; 
     421                 
     422                /* 
     423                 * Calculate new locations for the runs, based on how much space  
     424                 * we have remaining 
     425                 */              
     426                 
     427                ArrayList<DoublePoint> newLocations = new ArrayList<DoublePoint> (this.locations.size()); 
     428                int pos = 0, placed = 0; 
     429                 
     430                double remWidth = totalWidth - this.textWidth, averageW = (this.wordPos.length == 0 ? 0 : remWidth / (this.wordPos.length)); 
     431                //do stuff with locations 
     432                for (int i = 0; i < this.textRuns.size(); ++i) { 
     433                         
     434                        boolean found = (pos != 0 && Arrays.binarySearch (this.wordPos, pos) >= 0); 
     435                        placed += (found ? 1 : 0); 
     436                                         
     437                        DoublePoint element = this.locations.get(i); 
     438                        DoublePoint newElem = new DoublePoint (element.getX() + placed*averageW, element.getY()); 
     439                        newLocations.add(newElem);                               
     440                         
     441                        pos += this.textRuns.get(i).getRunLength(); 
     442                } 
     443                result = new HotSegmentLayout (this.textRuns, newLocations, this.wordPos, this.xOffset, this.textWidth); 
     444                 
     445                return result; 
    396446        } 
    397447}