| /* |
| * 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; |
| } |
| } |
| |
| |
| } |