/* * 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.svg.font; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; import java.util.Collections; import java.util.List; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.fop.complexscripts.util.CharAssociation; import org.apache.fop.complexscripts.util.CharMirror; import org.apache.fop.fonts.Font; class ComplexGlyphVector extends FOPGVTGlyphVector { public static final AttributedCharacterIterator.Attribute WRITING_MODE = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE; public static final Integer WRITING_MODE_RTL = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_RTL; private boolean reversed; // true if this GV was reversed private boolean mirrored; // true if this GV required some mirroring ComplexGlyphVector(FOPGVTFont font, final CharacterIterator iter, FontRenderContext frc) { super(font, iter, frc); } public void performDefaultLayout() { super.performDefaultLayout(); } public boolean isReversed() { return reversed; } public void maybeReverse(boolean mirror) { if (!reversed) { if (glyphs != null) { if (glyphs.length > 1) { reverse(glyphs); if (associations != null) { Collections.reverse(associations); } if (gposAdjustments != null) { reverse(gposAdjustments); } if (positions != null) { reverse(positions); } if (boundingBoxes != null) { reverse(boundingBoxes); } if (glyphTransforms != null) { reverse(glyphTransforms); } if (glyphVisibilities != null) { reverse(glyphVisibilities); } } if (maybeMirror()) { mirrored = true; } } reversed = true; } } // For each mirrorable character in source text, perform substitution of // associated glyph with a mirrored glyph. N.B. The source text is NOT // modified, only the mapped glyphs. private boolean maybeMirror() { boolean mirrored = false; String s = text.subSequence(text.getBeginIndex(), text.getEndIndex()).toString(); if (CharMirror.hasMirrorable(s)) { String m = CharMirror.mirror(s); assert m.length() == s.length(); for (int i = 0, n = m.length(); i < n; ++i) { char cs = s.charAt(i); char cm = m.charAt(i); if (cm != cs) { if (substituteMirroredGlyph(i, cm)) { mirrored = true; } } } } return mirrored; } private boolean substituteMirroredGlyph(int index, char mirror) { Font f = font.getFont(); int gi = 0; for (CharAssociation ca : (List<CharAssociation>) associations) { if (ca.contained(index, 1)) { setGlyphCode(gi, f.mapChar(mirror)); return true; } else { ++gi; } } return false; } private static void reverse(boolean[] ba) { for (int i = 0, n = ba.length, m = n / 2; i < m; i++) { int k = n - i - 1; boolean t = ba [ k ]; ba [ k ] = ba [ i ]; ba [ i ] = t; } } private static void reverse(int[] ia) { for (int i = 0, n = ia.length, m = n / 2; i < m; i++) { int k = n - i - 1; int t = ia [ k ]; ia [ k ] = ia [ i ]; ia [ i ] = t; } } private static void reverse(int[][] iaa) { for (int i = 0, n = iaa.length, m = n / 2; i < m; i++) { int k = n - i - 1; int[] t = iaa [ k ]; iaa [ k ] = iaa [ i ]; iaa [ i ] = t; } } private static void reverse(float[] fa) { int skip = 2; int numPositions = fa.length / skip; for (int i = 0, n = numPositions, m = n / 2; i < m; ++i) { int j = n - i - 1; for (int k = 0; k < skip; ++k) { int l1 = i * skip + k; int l2 = j * skip + k; float t = fa [ l2 ]; fa [ l2 ] = fa [ l1 ]; fa [ l1 ] = t; } } float runAdvanceX = fa [ 0 ]; for (int i = 0, n = numPositions; i < n; ++i) { int k = i * 2; fa [ k + 0 ] = runAdvanceX - fa [ k + 0 ]; if (i > 0) { fa [ k - 1 ] = fa [ k + 1 ]; } } } private static void reverse(Rectangle2D[] ra) { for (int i = 0, n = ra.length, m = n / 2; i < m; i++) { int k = n - i - 1; Rectangle2D t = ra [ k ]; ra [ k ] = ra [ i ]; ra [ i ] = t; } } private static void reverse(AffineTransform[] ta) { for (int i = 0, n = ta.length, m = n / 2; i < m; i++) { int k = n - i - 1; AffineTransform t = ta [ k ]; ta [ k ] = ta [ i ]; ta [ i ] = t; } } }