/*
 * 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: WordArea.java 1761026 2016-09-16 12:50:43Z ssteiner $ */

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
/** * A string of characters without spaces */
public class WordArea extends InlineArea { private static final long serialVersionUID = 6444644662158970942L;
The text for this word area
/** The text for this word area */
protected String word;
An array of width for adjusting the individual letters (optional)
/** 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)
/** * 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)
/** * 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.
/** * A flag indicating whether the content of word is reversed in relation to * its original logical order. */
protected boolean reversed;
Create a word area
Params:
  • blockProgressionOffset – the offset for this area
  • level – the bidirectional embedding level (or -1 if not defined) for word as a group
  • word – the word string
  • letterAdjust – the letter adjust array (may be null)
  • levels – array of per-character (glyph) bidirectional levels, in case word area is heterogenously leveled
  • gposAdjustments – array of general position adjustments or null if none apply
  • reversed – true if word is known to be reversed at construction time
/** * 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) { 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; }
Create a word area
Params:
  • blockProgressionOffset – the offset for this area
  • level – the bidirectional embedding level (or -1 if not defined) for word as a group
  • word – the word string
  • letterAdjust – the letter adjust array (may be null)
  • levels – array of per-character (glyph) bidirectional levels, in case word area is heterogenously leveled
  • gposAdjustments – array of general position adjustments or null if none apply
/** * 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); }
Returns:Returns the word.
/** @return Returns the word. */
public String getWord() { return word; }
Returns:the array of letter adjust widths
/** @return the array of letter adjust widths */
public int[] getLetterAdjustArray() { return this.letterAdjust; }
Obtain per-character (glyph) bidi levels.
Returns:a (possibly empty) array of levels or null (if none resolved)
/** * Obtain per-character (glyph) bidi levels. * @return a (possibly empty) array of levels or null (if none resolved) */
public int[] getBidiLevels() { return levels; }

Obtain per-character (glyph) bidi levels over a specified subsequence.

If word has been reversed, then the subsequence is over the reversed word.

Params:
  • start – starting (inclusive) index of subsequence
  • end – ending (exclusive) index of subsequence
Returns:a (possibly null) array of per-character (glyph) levels over the specified sequence
/** * <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; } }

Obtain per-character (glyph) level at a specified index position.

If word has been reversed, then the position is relative to the reversed word.

Params:
  • position – the index of the (possibly reversed) character from which to obtain the level
Returns:a resolved bidirectional level or, if not specified, then -1
/** * <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.
Returns:a (possibly empty) array of adjustments, each having four elements, or null if no adjustments apply
/** * 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; }

Obtain per-character (glyph) position adjustments at a specified index position.

If word has been reversed, then the position is relative to the reversed word.

Params:
  • position – the index of the (possibly reversed) character from which to obtain the level
Returns:an array of adjustments or null if none applies
/** * <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; } }

Reverse characters and corresponding per-character levels and glyph position adjustments.

Params:
  • mirror – if true, then perform mirroring if mirrorred characters
/** * <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); } } }

Perform mirroring on mirrorable characters.

/** * <p>Perform mirroring on mirrorable characters.</p> */
public void mirror() { if (word.length() > 0) { word = CharMirror.mirror(word); } }

Determined if word has been reversed (in relation to original logical order).

If a word is reversed, then both its characters (glyphs) and corresponding per-character levels are in reverse order.

Note: this information is used in order to process non-spacing marks during rendering as well as provide hints for caret direction.

Returns:true if word is reversed
/** * <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; } /* * 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; } } }