- /*
- * 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.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Stack;
- import java.util.Vector;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.area.LineArea;
- import org.apache.fop.area.inline.InlineArea;
- import org.apache.fop.fo.pagination.PageSequence;
-
- // CSOFF: LineLengthCheck
-
- /**
- * <p>A utility class for performing bidirectional resolution processing.</p>
- *
- * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
- */
- public final class BidiResolver {
-
- /**
- * logging instance
- */
- private static final Log log = LogFactory.getLog(BidiResolver.class);
-
- private BidiResolver() {
- }
-
- /**
- * Resolve inline directionality.
- * @param ps a page sequence FO instance
- */
- public static void resolveInlineDirectionality(PageSequence ps) {
- if (log.isDebugEnabled()) {
- log.debug("BD: RESOLVE: " + ps);
- }
- // 1. collect delimited text ranges
- List ranges = ps.collectDelimitedTextRanges(new Stack());
- if (log.isDebugEnabled()) {
- dumpRanges("BD: RESOLVE: RANGES:", ranges);
- }
- // 2. prune empty ranges
- ranges = pruneEmptyRanges(ranges);
- if (log.isDebugEnabled()) {
- dumpRanges("BD: RESOLVE: PRUNED RANGES:", ranges);
- }
- // 3. resolve inline directionaly of unpruned ranges
- resolveInlineDirectionality(ranges);
- }
-
- /**
- * Reorder line area.
- * @param la a line area instance
- */
- public static void reorder(LineArea la) {
-
- // 1. collect inline levels
- List runs = collectRuns(la.getInlineAreas(), new Vector());
- if (log.isDebugEnabled()) {
- dumpRuns("BD: REORDER: INPUT:", runs);
- }
-
- // 2. split heterogeneous inlines
- runs = splitRuns(runs);
- if (log.isDebugEnabled()) {
- dumpRuns("BD: REORDER: SPLIT INLINES:", runs);
- }
-
- // 3. determine minimum and maximum levels
- int[] mm = computeMinMaxLevel(runs, null);
- if (log.isDebugEnabled()) {
- log.debug("BD: REORDER: { min = " + mm[0] + ", max = " + mm[1] + "}");
- }
-
- // 4. reorder from maximum level to minimum odd level
- int mn = mm[0];
- int mx = mm[1];
- if (mx > 0) {
- for (int l1 = mx, l2 = ((mn & 1) == 0) ? (mn + 1) : mn; l1 >= l2; l1--) {
- runs = reorderRuns(runs, l1);
- }
- }
- if (log.isDebugEnabled()) {
- dumpRuns("BD: REORDER: REORDERED RUNS:", runs);
- }
-
- // 5. reverse word consituents (characters and glyphs) while mirroring
- boolean mirror = true;
- reverseWords(runs, mirror);
- if (log.isDebugEnabled()) {
- dumpRuns("BD: REORDER: REORDERED WORDS:", runs);
- }
-
- // 6. replace line area's inline areas with reordered runs' inline areas
- replaceInlines(la, replicateSplitWords(runs));
- }
-
- private static void resolveInlineDirectionality(List ranges) {
- for (Iterator it = ranges.iterator(); it.hasNext(); ) {
- DelimitedTextRange r = (DelimitedTextRange) it.next();
- r.resolve();
- if (log.isDebugEnabled()) {
- log.debug(r);
- }
- }
- }
-
- private static List collectRuns(List inlines, List runs) {
- for (Iterator it = inlines.iterator(); it.hasNext(); ) {
- InlineArea ia = (InlineArea) it.next();
- runs = ia.collectInlineRuns(runs);
- }
- return runs;
- }
-
- private static List splitRuns(List runs) {
- List runsNew = new Vector();
- for (Iterator it = runs.iterator(); it.hasNext(); ) {
- InlineRun ir = (InlineRun) it.next();
- if (ir.isHomogenous()) {
- runsNew.add(ir);
- } else {
- runsNew.addAll(ir.split());
- }
- }
- if (!runsNew.equals(runs)) {
- runs = runsNew;
- }
- return runs;
- }
-
- private static int[] computeMinMaxLevel(List runs, int[] mm) {
- if (mm == null) {
- mm = new int[] {Integer.MAX_VALUE, Integer.MIN_VALUE};
- }
- for (Iterator it = runs.iterator(); it.hasNext(); ) {
- InlineRun ir = (InlineRun) it.next();
- ir.updateMinMax(mm);
- }
- return mm;
- }
- private static List reorderRuns(List runs, int level) {
- assert level >= 0;
- List runsNew = new Vector();
- for (int i = 0, n = runs.size(); i < n; i++) {
- InlineRun iri = (InlineRun) runs.get(i);
- if (iri.getMinLevel() < level) {
- runsNew.add(iri);
- } else {
- int s = i;
- int e = s;
- while (e < n) {
- InlineRun ire = (InlineRun) runs.get(e);
- if (ire.getMinLevel() < level) {
- break;
- } else {
- e++;
- }
- }
- if (s < e) {
- runsNew.addAll(reverseRuns(runs, s, e));
- }
- i = e - 1;
- }
- }
- if (!runsNew.equals(runs)) {
- runs = runsNew;
- }
- return runs;
- }
- private static List reverseRuns(List runs, int s, int e) {
- int n = e - s;
- Vector runsNew = new Vector(n);
- if (n > 0) {
- for (int i = 0; i < n; i++) {
- int k = (n - i - 1);
- InlineRun ir = (InlineRun) runs.get(s + k);
- ir.reverse();
- runsNew.add(ir);
- }
- }
- return runsNew;
- }
- private static void reverseWords(List runs, boolean mirror) {
- for (Iterator it = runs.iterator(); it.hasNext(); ) {
- InlineRun ir = (InlineRun) it.next();
- ir.maybeReverseWord(mirror);
- }
- }
- private static List replicateSplitWords(List runs) {
- // [TBD] for each run which inline word area appears multiple times in
- // runs, replicate that word
- return runs;
- }
- private static void replaceInlines(LineArea la, List runs) {
- List<InlineArea> inlines = new ArrayList<InlineArea>();
- for (Iterator it = runs.iterator(); it.hasNext(); ) {
- InlineRun ir = (InlineRun) it.next();
- inlines.add(ir.getInline());
- }
- la.setInlineAreas(unflattenInlines(inlines));
- }
- private static List unflattenInlines(List<InlineArea> inlines) {
- return new UnflattenProcessor(inlines) .unflatten();
- }
- private static void dumpRuns(String header, List runs) {
- log.debug(header);
- for (Iterator it = runs.iterator(); it.hasNext(); ) {
- InlineRun ir = (InlineRun) it.next();
- log.debug(ir);
- }
- }
- private static void dumpRanges(String header, List ranges) {
- log.debug(header);
- for (Iterator it = ranges.iterator(); it.hasNext(); ) {
- DelimitedTextRange r = (DelimitedTextRange) it.next();
- log.debug(r);
- }
- }
- private static List pruneEmptyRanges(List ranges) {
- Vector rv = new Vector();
- for (Iterator it = ranges.iterator(); it.hasNext(); ) {
- DelimitedTextRange r = (DelimitedTextRange) it.next();
- if (!r.isEmpty()) {
- rv.add(r);
- }
- }
- return rv;
- }
-
- }
|