/*
 * 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.complexscripts.bidi;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;

import org.apache.fop.area.inline.Anchor;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.UnresolvedPageNumber;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.util.CharUtilities;

The InlineRun class is a utility class, the instances of which are used to capture a sequence of reordering levels associated with an inline area.

This work was originally authored by Glenn Adams (gadams@apache.org).

/** * The <code>InlineRun</code> class is a utility class, the instances of which are used * to capture a sequence of reordering levels associated with an inline area. * * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p> */
public class InlineRun { private InlineArea inline; private int[] levels; private int minLevel; private int maxLevel; private int reversals;
Primary constructor.
Params:
  • inline – which generated this inline run
  • levels – levels array
/** * Primary constructor. * @param inline which generated this inline run * @param levels levels array */
public InlineRun(InlineArea inline, int[] levels) { assert inline != null; assert levels != null; this.inline = inline; this.levels = levels; setMinMax(levels); }
Alternate constructor.
Params:
  • inline – which generated this inline run
  • level – for each index
  • count – of indices
/** * Alternate constructor. * @param inline which generated this inline run * @param level for each index * @param count of indices */
public InlineRun(InlineArea inline, int level, int count) { this (inline, makeLevels(level, count)); }
Obtain inline area that generated this inline run.
Returns:inline area that generated this inline run.
/** * Obtain inline area that generated this inline run. * @return inline area that generated this inline run. */
public InlineArea getInline() { return inline; }
Obtain minimum bidi level for this run.
Returns:minimum bidi level
/** * Obtain minimum bidi level for this run. * @return minimum bidi level */
public int getMinLevel() { return minLevel; }
Obtain maximum bidi level for this run.
Returns:maximum bidi level
/** * Obtain maximum bidi level for this run. * @return maximum bidi level */
public int getMaxLevel() { return maxLevel; } private void setMinMax(int[] levels) { int mn = Integer.MAX_VALUE; int mx = Integer.MIN_VALUE; if ((levels != null) && (levels.length > 0)) { for (int l : levels) { if (l < mn) { mn = l; } if (l > mx) { mx = l; } } } else { mn = mx = -1; } this.minLevel = mn; this.maxLevel = mx; }
Determine if this run has homogenous (same valued) bidi levels.
Returns:true if homogenous
/** * Determine if this run has homogenous (same valued) bidi levels. * @return true if homogenous */
public boolean isHomogenous() { return minLevel == maxLevel; }
Split this inline run into homogenous runs.
Returns:list of new runs
/** * Split this inline run into homogenous runs. * @return list of new runs */
public List split() { List runs = new Vector(); for (int i = 0, n = levels.length; i < n; ) { int l = levels [ i ]; int s = i; int e = s; while (e < n) { if (levels [ e ] != l) { break; } else { e++; } } if (s < e) { runs.add(new InlineRun(inline, l, e - s)); } i = e; } assert runs.size() < 2 : "heterogeneous inlines not yet supported!!"; return runs; }
Update a min/max array to correspond with this run's min/max values.
Params:
  • mm – reference to min/max array
/** * Update a min/max array to correspond with this run's min/max values. * @param mm reference to min/max array */
public void updateMinMax(int[] mm) { if (minLevel < mm[0]) { mm[0] = minLevel; } if (maxLevel > mm[1]) { mm[1] = maxLevel; } }
Determine if run needs mirroring.
Returns:true if run is homogenous and (positive) odd (i.e., right to left)
/** * Determine if run needs mirroring. * @return true if run is homogenous and (positive) odd (i.e., right to left) */
public boolean maybeNeedsMirroring() { return (minLevel == maxLevel) && (minLevel > 0) && ((minLevel & 1) != 0); }
Reverse run (by incrementing reversal count, not actually reversing).
/** * Reverse run (by incrementing reversal count, not actually reversing). */
public void reverse() { reversals++; }
Reverse inline area if it is a word area and it requires reversal.
Params:
  • mirror – if true then also mirror characters
/** * Reverse inline area if it is a word area and it requires * reversal. * @param mirror if true then also mirror characters */
public void maybeReverseWord(boolean mirror) { if (inline instanceof WordArea) { WordArea w = (WordArea) inline; // if not already reversed, then reverse now if (!w.isReversed()) { if ((reversals & 1) != 0) { w.reverse(mirror); } else if (mirror && maybeNeedsMirroring()) { w.mirror(); } } } } @Override public boolean equals(Object o) { if (o instanceof InlineRun) { InlineRun ir = (InlineRun) o; if (ir.inline != inline) { return false; } else if (ir.minLevel != minLevel) { return false; } else if (ir.maxLevel != maxLevel) { return false; } else if ((ir.levels != null) && (levels != null)) { if (ir.levels.length != levels.length) { return false; } else { for (int i = 0, n = levels.length; i < n; i++) { if (ir.levels[i] != levels[i]) { return false; } } return true; } } else { return (ir.levels == null) && (levels == null); } } else { return false; } } @Override public int hashCode() { int l = (inline != null) ? inline.hashCode() : 0; l = (l ^ minLevel) + (l << 19); l = (l ^ maxLevel) + (l << 11); return l; } @Override public String toString() { StringBuffer sb = new StringBuffer("RR: { type = \'"); char c; String content = null; if (inline instanceof WordArea) { c = 'W'; content = ((WordArea) inline) .getWord(); } else if (inline instanceof SpaceArea) { c = 'S'; content = ((SpaceArea) inline) .getSpace(); } else if (inline instanceof Anchor) { c = 'A'; } else if (inline instanceof Leader) { c = 'L'; } else if (inline instanceof Space) { c = 'S'; } else if (inline instanceof UnresolvedPageNumber) { c = '#'; content = ((UnresolvedPageNumber) inline) .getText(); } else if (inline instanceof InlineBlockParent) { c = 'B'; } else if (inline instanceof InlineViewport) { c = 'V'; } else if (inline instanceof InlineParent) { c = 'I'; } else { c = '?'; } sb.append(c); sb.append("\', levels = \'"); sb.append(generateLevels(levels)); sb.append("\', min = "); sb.append(minLevel); sb.append(", max = "); sb.append(maxLevel); sb.append(", reversals = "); sb.append(reversals); sb.append(", content = <"); sb.append(CharUtilities.toNCRefs(content)); sb.append("> }"); return sb.toString(); } private String generateLevels(int[] levels) { StringBuffer lb = new StringBuffer(); int maxLevel = -1; int numLevels = levels.length; for (int l : levels) { if (l > maxLevel) { maxLevel = l; } } if (maxLevel < 0) { // leave level buffer empty } else if (maxLevel < 10) { // use string of decimal digits for (int level : levels) { lb.append((char) ('0' + level)); } } else { // use comma separated list boolean first = true; for (int level : levels) { if (first) { first = false; } else { lb.append(','); } lb.append(level); } } return lb.toString(); } private static int[] makeLevels(int level, int count) { int[] levels = new int [ count > 0 ? count : 1 ]; Arrays.fill(levels, level); return levels; } }