123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
- /* $Id$ */
-
- package org.apache.fop.area.inline;
-
- import java.util.Arrays;
- import java.util.List;
-
- import org.apache.fop.complexscripts.bidi.InlineRun;
- import org.apache.fop.complexscripts.util.CharMirror;
-
- /**
- * A string of characters without spaces
- */
- public class WordArea extends InlineArea {
-
- private static final long serialVersionUID = 6444644662158970942L;
-
- /** The text for this word area */
- protected String word;
-
- /** An array of width for adjusting the individual letters (optional) */
- protected int[] letterAdjust;
-
- /**
- * An array of resolved bidirectional levels corresponding to each character
- * in word (optional)
- */
- protected int[] levels;
-
- /**
- * An array of glyph positioning adjustments to apply to each glyph 'char' in word (optional)
- */
- protected int[][] gposAdjustments;
-
- /**
- * A flag indicating whether the content of word is reversed in relation to
- * its original logical order.
- */
- protected boolean reversed;
- private boolean nextIsSpace;
-
- /**
- * Create a word area
- * @param blockProgressionOffset the offset for this area
- * @param level the bidirectional embedding level (or -1 if not defined) for word as a group
- * @param word the word string
- * @param letterAdjust the letter adjust array (may be null)
- * @param levels array of per-character (glyph) bidirectional levels,
- * in case word area is heterogenously leveled
- * @param gposAdjustments array of general position adjustments or null if none apply
- * @param reversed true if word is known to be reversed at construction time
- */
- public WordArea(int blockProgressionOffset, int level, String word, int[] letterAdjust, int[] levels,
- int[][] gposAdjustments, boolean reversed, boolean nextIsSpace) {
- super(blockProgressionOffset, level);
- int length = (word != null) ? word.length() : 0;
- this.word = word;
- this.letterAdjust = maybeAdjustLength(letterAdjust, length);
- this.levels = maybePopulateLevels(levels, level, length);
- this.gposAdjustments = maybeAdjustLength(gposAdjustments, length);
- this.reversed = reversed;
- this.nextIsSpace = nextIsSpace;
- }
-
- public WordArea(int blockProgressionOffset, int level, String word, int[] letterAdjust, int[] levels,
- int[][] gposAdjustments, boolean reversed) {
- this(blockProgressionOffset, level, word, letterAdjust, levels, gposAdjustments, reversed, false);
- }
-
- /**
- * Create a word area
- * @param blockProgressionOffset the offset for this area
- * @param level the bidirectional embedding level (or -1 if not defined) for word as a group
- * @param word the word string
- * @param letterAdjust the letter adjust array (may be null)
- * @param levels array of per-character (glyph) bidirectional levels,
- * in case word area is heterogenously leveled
- * @param gposAdjustments array of general position adjustments or null if none apply
- */
- public WordArea(
- int blockProgressionOffset, int level, String word, int[] letterAdjust, int[] levels,
- int[][] gposAdjustments) {
- this (blockProgressionOffset, level, word, letterAdjust, levels, gposAdjustments, false);
- }
-
- /** @return Returns the word. */
- public String getWord() {
- return word;
- }
-
- /** @return the array of letter adjust widths */
- public int[] getLetterAdjustArray() {
- return this.letterAdjust;
- }
-
- /**
- * Obtain per-character (glyph) bidi levels.
- * @return a (possibly empty) array of levels or null (if none resolved)
- */
- public int[] getBidiLevels() {
- return levels;
- }
-
- /**
- * <p>Obtain per-character (glyph) bidi levels over a specified subsequence.</p>
- * <p>If word has been reversed, then the subsequence is over the reversed word.</p>
- * @param start starting (inclusive) index of subsequence
- * @param end ending (exclusive) index of subsequence
- * @return a (possibly null) array of per-character (glyph) levels over the specified
- * sequence
- */
- public int[] getBidiLevels(int start, int end) {
- assert start <= end;
- if (this.levels != null) {
- int n = end - start;
- int[] levels = new int [ n ];
- System.arraycopy(this.levels, start + 0, levels, 0, n);
- return levels;
- } else {
- return null;
- }
- }
-
- /**
- * <p>Obtain per-character (glyph) level at a specified index position.</p>
- * <p>If word has been reversed, then the position is relative to the reversed word.</p>
- * @param position the index of the (possibly reversed) character from which to obtain the
- * level
- * @return a resolved bidirectional level or, if not specified, then -1
- */
- public int bidiLevelAt(int position) {
- if (position > word.length()) {
- throw new IndexOutOfBoundsException();
- } else if (levels != null) {
- return levels [ position ];
- } else {
- return -1;
- }
- }
-
- @Override
- public List collectInlineRuns(List runs) {
- assert runs != null;
- InlineRun r;
- int[] levels = getBidiLevels();
- if ((levels != null) && (levels.length > 0)) {
- r = new InlineRun(this, levels);
- } else {
- r = new InlineRun(this, getBidiLevel(), word.length());
- }
- runs.add(r);
- return runs;
- }
-
- /**
- * Obtain per-character (glyph) position adjustments.
- * @return a (possibly empty) array of adjustments, each having four elements, or null
- * if no adjustments apply
- */
- public int[][] getGlyphPositionAdjustments() {
- return gposAdjustments;
- }
-
- /**
- * <p>Obtain per-character (glyph) position adjustments at a specified index position.</p>
- * <p>If word has been reversed, then the position is relative to the reversed word.</p>
- * @param position the index of the (possibly reversed) character from which to obtain the
- * level
- * @return an array of adjustments or null if none applies
- */
- public int[] glyphPositionAdjustmentsAt(int position) {
- if (position > word.length()) {
- throw new IndexOutOfBoundsException();
- } else if (gposAdjustments != null) {
- return gposAdjustments [ position ];
- } else {
- return null;
- }
- }
-
- /**
- * <p>Reverse characters and corresponding per-character levels and glyph position
- * adjustments.</p>
- * @param mirror if true, then perform mirroring if mirrorred characters
- */
- public void reverse(boolean mirror) {
- if (word.length() > 0) {
- word = ((new StringBuffer(word)) .reverse()) .toString();
- if (levels != null) {
- reverse(levels);
- }
- if (gposAdjustments != null) {
- reverse(gposAdjustments);
- }
- reversed = !reversed;
- if (mirror) {
- word = CharMirror.mirror(word);
- }
- }
- }
-
- /**
- * <p>Perform mirroring on mirrorable characters.</p>
- */
- public void mirror() {
- if (word.length() > 0) {
- word = CharMirror.mirror(word);
- }
- }
-
- /**
- * <p>Determined if word has been reversed (in relation to original logical order).</p>
- * <p>If a word is reversed, then both its characters (glyphs) and corresponding per-character
- * levels are in reverse order.</p>
- * <p>Note: this information is used in order to process non-spacing marks during rendering as
- * well as provide hints for caret direction.</p>
- * @return true if word is reversed
- */
- public boolean isReversed() {
- return reversed;
- }
-
- public boolean isNextIsSpace() {
- return nextIsSpace;
- }
-
- /*
- * If int[] array is not of specified length, then create
- * a new copy of the first length entries.
- */
- private static int[] maybeAdjustLength(int[] ia, int length) {
- if (ia != null) {
- if (ia.length == length) {
- return ia;
- } else {
- int[] iaNew = new int [ length ];
- for (int i = 0, n = ia.length; i < n; i++) {
- if (i < length) {
- iaNew [ i ] = ia [ i ];
- } else {
- break;
- }
- }
- return iaNew;
- }
- } else {
- return ia;
- }
- }
-
- /*
- * If int[][] matrix is not of specified length, then create
- * a new shallow copy of the first length entries.
- */
- private static int[][] maybeAdjustLength(int[][] im, int length) {
- if (im != null) {
- if (im.length == length) {
- return im;
- } else {
- int[][] imNew = new int [ length ][];
- for (int i = 0, n = im.length; i < n; i++) {
- if (i < length) {
- imNew [ i ] = im [ i ];
- } else {
- break;
- }
- }
- return imNew;
- }
- } else {
- return im;
- }
- }
-
- private static int[] maybePopulateLevels(int[] levels, int level, int count) {
- if ((levels == null) && (level >= 0)) {
- levels = new int[count];
- Arrays.fill(levels, level);
- }
- return maybeAdjustLength(levels, count);
- }
-
- private static void reverse(int[] a) {
- for (int i = 0, n = a.length, m = n / 2; i < m; i++) {
- int k = n - i - 1;
- int t = a [ k ];
- a [ k ] = a [ i ];
- a [ i ] = t;
- }
- }
-
- private static void reverse(int[][] aa) {
- for (int i = 0, n = aa.length, m = n / 2; i < m; i++) {
- int k = n - i - 1;
- int[] t = aa [ k ];
- aa [ k ] = aa [ i ];
- aa [ i ] = t;
- }
- }
-
- }
|