Author: ssteiner
Date: Tue Nov 20 12:35:21 2018
New Revision: 1846994

URL: http://svn.apache.org/viewvc?rev=1846994&view=rev
Log:
FOP-2827: Add support for Khmer complex script

Added:
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java
   (with props)
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java
   (with props)
    
xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java
   (with props)
Modified:
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/CharScript.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/GlyphSequence.java
    
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
 Tue Nov 20 12:35:21 2018
@@ -731,7 +731,8 @@ public class GlyphPositioningTable exten
                 if (ma != null) {
                     for (int i = 0, n = ps.getPosition(); i < n; i++) {
                         int gi = ps.getGlyph(-(i + 1));
-                        if (ps.isMark(gi)) {
+                        int unprocessedGlyph = ps.getUnprocessedGlyph(-(i + 
1));
+                        if (ps.isMark(gi) && ps.isMark(unprocessedGlyph)) {
                             continue;
                         } else {
                             Anchor a = getBaseAnchor(gi, ma.getMarkClass());
@@ -743,6 +744,9 @@ public class GlyphPositioningTable exten
                                     v.adjust(0, 0, -ps.getWidth(giMark), 0);
                                 }
                                 // end experimental fix for END OF AYAH in 
Lateef/Scheherazade
+                                if (OTFScript.KHMER.equals(ps.script)) {
+                                    v.adjust(-ps.getWidth(gi), -v.yPlacement, 
0, 0);
+                                }
                                 if (ps.adjust(v)) {
                                     ps.setAdjusted(true);
                                 }
@@ -875,13 +879,13 @@ public class GlyphPositioningTable exten
                 int mxc = getMaxComponentCount();
                 if (ma != null) {
                     for (int i = 0, n = ps.getPosition(); i < n; i++) {
-                        int gi = ps.getGlyph(-(i + 1));
-                        if (ps.isMark(gi)) {
+                        int glyphIndex = ps.getUnprocessedGlyph(-(i + 1));
+                        if (ps.isMark(glyphIndex)) {
                             continue;
                         } else {
-                            Anchor a = getLigatureAnchor(gi, mxc, i, 
ma.getMarkClass());
-                            if (a != null) {
-                                if (ps.adjust(a.getAlignmentAdjustment(ma))) {
+                            Anchor anchor = getLigatureAnchor(glyphIndex, mxc, 
i, ma.getMarkClass());
+                            if (anchor != null) {
+                                if 
(ps.adjust(anchor.getAlignmentAdjustment(ma))) {
                                     ps.setAdjusted(true);
                                 }
                             }
@@ -1033,9 +1037,9 @@ public class GlyphPositioningTable exten
                 MarkAnchor ma = getMark1Anchor(ciMark1, giMark1);
                 if (ma != null) {
                     if (ps.hasPrev()) {
-                        Anchor a = getMark2Anchor(ps.getGlyph(-1), 
ma.getMarkClass());
-                        if (a != null) {
-                            if (ps.adjust(a.getAlignmentAdjustment(ma))) {
+                        Anchor anchor = 
getMark2Anchor(ps.getUnprocessedGlyph(-1), ma.getMarkClass());
+                        if (anchor != null) {
+                            if (ps.adjust(anchor.getAlignmentAdjustment(ma))) {
                                 ps.setAdjusted(true);
                             }
                         }

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
 Tue Nov 20 12:35:21 2018
@@ -413,6 +413,15 @@ public class GlyphProcessingState {
         }
     }
 
+    public int getUnprocessedGlyph(int offset) throws 
IndexOutOfBoundsException {
+        int i = index + offset;
+        if ((i >= 0) && (i < indexLast)) {
+            return igs.getUnprocessedGlyph(i);
+        } else {
+            throw new IndexOutOfBoundsException("Attempting to process glyph 
at index " + i);
+        }
+    }
+
     /**
      * Obtain glyph at current position.
      * @return glyph at current position

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
 Tue Nov 20 12:35:21 2018
@@ -31,6 +31,7 @@ import org.apache.fop.complexscripts.scr
 import org.apache.fop.complexscripts.util.CharAssociation;
 import org.apache.fop.complexscripts.util.GlyphSequence;
 import org.apache.fop.complexscripts.util.GlyphTester;
+import org.apache.fop.fonts.MultiByteFont;
 
 // CSOFF: LineLengthCheck
 
@@ -105,6 +106,11 @@ public class GlyphSubstitutionTable exte
         return ogs;
     }
 
+    public CharSequence preProcess(CharSequence charSequence, String script, 
MultiByteFont font, List associations) {
+        ScriptProcessor scriptProcessor = ScriptProcessor.getInstance(script, 
processors);
+        return scriptProcessor.preProcess(charSequence, font, associations);
+    }
+
     /**
      * Map a lookup type name to its constant (integer) value.
      * @param name lookup type name

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
 Tue Nov 20 12:35:21 2018
@@ -130,6 +130,8 @@ public class IndicScriptProcessor extend
         case CharScript.SCRIPT_TAMIL:
         case CharScript.SCRIPT_TAMIL_2:
             return new TamilScriptProcessor(script);
+        case CharScript.SCRIPT_KHMER:
+            return new KhmerScriptProcessor(script);
         // [TBD] implement other script processors
         default:
             return new IndicScriptProcessor(script);
@@ -239,6 +241,7 @@ public class IndicScriptProcessor extend
         "rkrf",
         "rphf",
         "vatu",
+        "ccmp"
     };
     static {
         basicShapingFeatures = new HashSet<String>();
@@ -261,6 +264,7 @@ public class IndicScriptProcessor extend
         "haln",
         "pres",
         "psts",
+        "clig"
     };
     static {
         presentationFeatures = new HashSet<String>();

Added: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java?rev=1846994&view=auto
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java
 (added)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java
 Tue Nov 20 12:35:21 2018
@@ -0,0 +1,372 @@
+/*
+ * 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.scripts;
+
+/**
+ * Integrating existing rendering of Android for Khmer Unicode to iText
+ *    The class from the rendering of Mobile Project, Android from Nokor Group 
(AKA: Nokor-IT)
+ *    The understanding also taking from the Khmum Browser that would lead to 
build this helper
+ *    (Comment above by Pongsametrey S. <met...@osify.com>)
+ *    Thanks for Nokor Group & Mr. Pengleng HUOT
+ *
+ * author sok.pongsametrey
+ * @version 1.0
+ */
+
+/**
+ * UnicodeRender Class.
+ * author huot.pengleng
+ *
+ * simple classes, they are used in the state table (in this file) to control 
the length of a syllable
+ * they are also used to know where a character should be placed (location in 
reference to the base character)
+ * and also to know if a character, when independently displayed, should be 
displayed with a dotted-circle to
+ * indicate error in syllable construction
+ * Character class tables
+ * xx character does not combine into syllable, such as numbers, puntuation 
marks, non-Khmer signs...
+ * sa Sign placed above the base
+ * sp Sign placed after the base
+ * c1 Consonant of type 1 or independent vowel (independent vowels behave as 
type 1 consonants)
+ * c2 Consonant of type 2 (only RO)
+ * c3 Consonant of type 3
+ * rb Khmer sign robat u17CC. combining mark for subscript consonants
+ * cd Consonant-shifter
+ * dl Dependent vowel placed before the base (left of the base)
+ * db Dependent vowel placed below the base
+ * da Dependent vowel placed above the base
+ * dr Dependent vowel placed behind the base (right of the base)
+ * co Khmer combining mark COENG u17D2, combines with the consonant or 
independent vowel following
+ *     it to create a subscript consonant or independent vowel
+ * va Khmer split vowel in wich the first part is before the base and the 
second one above the base
+ * vr Khmer split vowel in wich the first part is before the base and the 
second one behind (right of) the base
+ *
+ */
+public class KhmerRenderer {
+
+    private static final int XX = 0;
+    private static final int CC_COENG = 7; // Subscript consonant combining 
character
+    private static final int CC_CONSONANT = 1; // Consonant of type 1 or 
independent vowel
+    private static final int CC_CONSONANT_SHIFTER = 5;
+    private static final int CC_CONSONANT2 = 2; // Consonant of type 2
+    private static final int CC_CONSONANT3 = 3; // Consonant of type 3
+    private static final int CC_DEPENDENT_VOWEL = 8;
+    private static final int CC_ROBAT = 6; // Khmer special diacritic accent 
-treated differently in state table
+    private static final int CC_SIGN_ABOVE = 9;
+    private static final int CC_SIGN_AFTER = 10;
+    private static final int CF_ABOVE_VOWEL = 536870912; // flag to speed up 
comparing
+    private static final int CF_CLASS_MASK = 65535;
+    private static final int CF_COENG = 134217728; // flag to speed up 
comparing
+    private static final int CF_CONSONANT = 16777216; // flag to speed up 
comparing
+    private static final int CF_DOTTED_CIRCLE = 67108864;
+
+    // add a dotted circle if a character with this flag is the first in a 
syllable
+    private static final int CF_POS_ABOVE = 131072;
+    private static final int CF_POS_AFTER = 65536;
+    private static final int CF_POS_BEFORE = 524288;
+    private static final int CF_POS_BELOW = 262144;
+    private static final int CF_SHIFTER = 268435456; // flag to speed up 
comparing
+    private static final int CF_SPLIT_VOWEL = 33554432;
+    private static final int C1 = CC_CONSONANT + CF_CONSONANT;
+    private static final int C2 = CC_CONSONANT2 + CF_CONSONANT;
+    private static final int C3 = CC_CONSONANT3 + CF_CONSONANT;
+    private static final int CO = CC_COENG + CF_COENG + CF_DOTTED_CIRCLE;
+    private static final int CS = CC_CONSONANT_SHIFTER + CF_DOTTED_CIRCLE + 
CF_SHIFTER;
+    private static final int DA = CC_DEPENDENT_VOWEL + CF_POS_ABOVE + 
CF_DOTTED_CIRCLE + CF_ABOVE_VOWEL;
+    private static final int DB = CC_DEPENDENT_VOWEL + CF_POS_BELOW + 
CF_DOTTED_CIRCLE;
+    private static final int DL = CC_DEPENDENT_VOWEL + CF_POS_BEFORE + 
CF_DOTTED_CIRCLE;
+    private static final int DR = CC_DEPENDENT_VOWEL + CF_POS_AFTER + 
CF_DOTTED_CIRCLE;
+    private static final int RB = CC_ROBAT + CF_POS_ABOVE + CF_DOTTED_CIRCLE;
+    private static final int SA = CC_SIGN_ABOVE + CF_DOTTED_CIRCLE + 
CF_POS_ABOVE;
+    private static final int SP = CC_SIGN_AFTER + CF_DOTTED_CIRCLE + 
CF_POS_AFTER;
+    private static final int VA = DA + CF_SPLIT_VOWEL;
+    private static final int VR = DR + CF_SPLIT_VOWEL;
+    // flag for a split vowel -> the first part is added in front of the 
syllable
+    private static final char BA = '\u1794';
+    private static final char COENG = '\u17D2';
+    private static final String CONYO = 
Character.toString('\u17D2').concat(Character.toString('\u1789'));
+    private static final String CORO = 
Character.toString('\u17D2').concat(Character.toString('\u179A'));
+
+    private int[] khmerCharClasses = new int[] {
+            C1, C1, C1, C3, C1, C1, C1, C1, C3, C1, C1, C1, C1, C3, C1, C1, 
C1, C1, C1, C1, C3,
+            C1, C1, C1, C1, C3, C2, C1, C1, C1, C3, C3, C1, C3, C1, C1, C1, 
C1, C1, C1, C1, C1,
+            C1, C1, C1, C1, C1, C1, C1, C1, C1, C1, DR, DR, DR, DA, DA, DA, 
DA, DB, DB, DB, VA,
+            VR, VR, DL, DL, DL, VR, VR, SA, SP, SP, CS, CS, SA, RB, SA, SA, 
SA, SA, SA, CO, SA,
+            XX, XX, XX, XX, XX, XX, XX, XX, XX, SA, XX, XX
+    };
+    private short[][] khmerStateTable = new short[][] {
+            {
+                    1, 2, 2, 2, 1, 1, 1, 6, 1, 1, 1, 2
+            }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, 3, 4, 5, 6, 16, 17, 1, -1
+    }, {
+            -1, -1, -1, -1, -1, 4, -1, -1, 16, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, 15, -1, -1, 6, 16, 17, 1, 14
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, 20, -1, 1, -1
+    }, {
+            -1, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, 12, 13, -1, 10, 16, 17, 1, 14
+    }, {
+            -1, -1, -1, -1, 12, 13, -1, -1, 16, 17, 1, 14
+    }, {
+            -1, -1, -1, -1, 12, 13, -1, 10, 16, 17, 1, 14
+    }, {
+            -1, 11, 11, 11, -1, -1, -1, -1, -1, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, 15, -1, -1, -1, 16, 17, 1, 14
+    }, {
+            -1, -1, -1, -1, -1, 13, -1, -1, 16, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, 15, -1, -1, -1, 16, 17, 1, 14
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 1, 18
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 18
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, 19, -1, -1, -1, -1
+    }, {
+            -1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1
+    }, {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1
+    }
+    };
+    private static final char MARK = '\u17EA';
+    private static final char NYO = '\u1789';
+    private static final char SA_C = '\u179F';
+    private static final char SRAAA = '\u17B6';
+    private static final char SRAAU = '\u17C5';
+    private static final char SRAE = '\u17C1';
+    private static final char SRAIE = '\u17C0';
+    private static final char SRAII = '\u17B8';
+    private static final char SRAOE = '\u17BE';
+    private static final char SRAOO = '\u17C4';
+    private static final char SRAU = '\u17BB';
+    private static final char SRAYA = '\u17BF';
+    private static final char TRIISAP = '\u17CA';
+    private static final char YO = '\u1799';
+
+    private char strEcombining(final char chrInput) {
+        char retChar = ' ';
+        if (chrInput == SRAOE) {
+            retChar = SRAII;
+        } else if (chrInput == SRAYA) {
+            retChar = SRAYA;
+        } else if (chrInput == SRAIE) {
+            retChar = SRAIE;
+        } else if (chrInput == SRAOO) {
+            retChar = SRAAA;
+        } else if (chrInput == SRAAU) {
+            retChar = SRAAU;
+        }
+
+        return retChar;
+    }
+
+    // Gets the charactor class.
+    private int getCharClass(final char uniChar) {
+        int retValue = 0;
+        int ch;
+        ch = uniChar;
+        if (ch > 255) {
+            if (ch >= '\u1780') {
+                ch -= '\u1780';
+                if (ch < khmerCharClasses.length) {
+                    retValue = khmerCharClasses[ch];
+                }
+            }
+        }
+        return retValue;
+    }
+
+    /**
+     * Re-order Khmer unicode for display with Khmer.ttf file on Android.
+     * @param strInput Khmer unicode string.
+     * @return String after render.
+     */
+    public String render(final String strInput) {
+        //Given an input String of unicode cluster to reorder.
+        //The return is the visual based cluster (legacy style) String.
+
+        int cursor = 0;
+        short state = 0;
+        int charCount = strInput.length();
+        StringBuilder result = new StringBuilder();
+
+        while (cursor < charCount) {
+            String reserved = "";
+            String signAbove = "";
+            String signAfter = "";
+            String base = "";
+            String robat = "";
+            String shifter = "";
+            String vowelBefore = "";
+            String vowelBelow = "";
+            String vowelAbove = "";
+            String vowelAfter = "";
+            boolean coeng = false;
+            String cluster;
+
+            String coeng1 = "";
+            String coeng2 = "";
+
+            boolean shifterAfterCoeng = false;
+
+            while (cursor < charCount) {
+                char curChar = strInput.charAt(cursor);
+                int kChar = getCharClass(curChar);
+                int charClass = kChar & CF_CLASS_MASK;
+                try {
+                    state = khmerStateTable[state][charClass];
+                } catch (Exception ex) {
+                    state = -1;
+                }
+
+                if (state < 0) {
+                    break;
+                }
+
+                //collect variable for cluster here
+
+                if (kChar == XX) {
+                    reserved = Character.toString(curChar);
+                } else if (kChar == SA) { //Sign placed above the base
+                    signAbove = Character.toString(curChar);
+                } else if (kChar == SP) { //Sign placed after the base
+                    signAfter = Character.toString(curChar);
+                } else if (kChar == C1 || kChar == C2 || kChar == C3) { 
//Consonant
+                    if (coeng) {
+                        if ("".equalsIgnoreCase(coeng1)) {
+                            coeng1 = 
Character.toString(COENG).concat(Character.toString(curChar));
+                        } else {
+                            coeng2 = 
Character.toString(COENG).concat(Character.toString(curChar));
+                        }
+                        coeng = false;
+                    } else {
+                        base = Character.toString(curChar);
+                    }
+                } else if (kChar == RB) { //Khmer sign robat u17CC
+                    robat = Character.toString(curChar);
+                } else if (kChar == CS) { //Consonant-shifter
+                    if (!"".equalsIgnoreCase(coeng1)) {
+                        shifterAfterCoeng = true;
+                    }
+
+                    shifter = Character.toString(curChar);
+                } else if (kChar == DL) { //Dependent vowel placed before the 
base
+                    vowelBefore = Character.toString(curChar);
+                } else if (kChar == DB) { //Dependent vowel placed below the 
base
+                    vowelBelow = Character.toString(curChar);
+                } else if (kChar == DA) { //Dependent vowel placed above the 
base
+                    vowelAbove = Character.toString(curChar);
+                } else if (kChar == DR) { //Dependent vowel placed behind the 
base
+                    vowelAfter = Character.toString(curChar);
+                } else if (kChar == CO) { //Khmer combining mark COENG
+                    coeng = true;
+                } else if (kChar == VA) { //Khmer split vowel, see da
+                    vowelBefore = Character.toString(SRAE);
+                    vowelAbove = Character.toString(strEcombining(curChar));
+                } else if (kChar == VR) { //Khmer split vowel, see dr
+                    vowelBefore = Character.toString(SRAE);
+                    vowelAfter = Character.toString(strEcombining(curChar));
+                }
+
+                cursor += 1;
+            }
+            // end of while (a cluster has found)
+
+            // logic when cluster has coeng
+            // should coeng be located on left side
+            String coengBefore = "";
+            if (CORO.equalsIgnoreCase(coeng1)) {
+                coengBefore = coeng1;
+                coeng1 = "";
+            } else if (CORO.equalsIgnoreCase(coeng2)) {
+                coengBefore = coeng2;
+                coeng2 = "";
+            }
+
+            //logic of shifter with base character
+            if (!"".equalsIgnoreCase(base) && !"".equalsIgnoreCase(shifter)) {
+                if (!"".equalsIgnoreCase(vowelAbove)) {
+                    shifter = "";
+                    vowelBelow = Character.toString(SRAU);
+                }
+            }
+
+            // uncomplete coeng
+            if (coeng && "".equalsIgnoreCase(coeng1)) {
+                coeng1 = Character.toString(COENG);
+            } else if (coeng && "".equalsIgnoreCase(coeng2)) {
+                coeng2 = 
Character.toString(MARK).concat(Character.toString(COENG));
+            }
+
+            //place of shifter
+            String shifter1 = "";
+            String shifter2 = "";
+
+            if (shifterAfterCoeng) {
+                shifter2 = shifter;
+            } else {
+                shifter1 = shifter;
+            }
+
+            boolean specialCaseBA = false;
+            String strMARKSRAAA = 
Character.toString(MARK).concat(Character.toString(SRAAA));
+            String strMARKSRAAU = 
Character.toString(MARK).concat(Character.toString(SRAAU));
+
+            if (Character.toString(BA).equalsIgnoreCase(base)
+                    && (Character.toString(SRAAA).equalsIgnoreCase(vowelAfter)
+                    || Character.toString(SRAAU).equalsIgnoreCase(vowelAfter)
+                    || strMARKSRAAA.equalsIgnoreCase(vowelAfter) || 
strMARKSRAAU.equalsIgnoreCase(vowelAfter))) {
+                specialCaseBA = true;
+
+                if (!"".equalsIgnoreCase(coeng1)) {
+                    String coeng1Complete = coeng1.substring(0, 
coeng1.length() - 1);
+                    if (Character.toString(BA).equalsIgnoreCase(coeng1Complete)
+                            || 
Character.toString(YO).equalsIgnoreCase(coeng1Complete)
+                            || 
Character.toString(SA_C).equalsIgnoreCase(coeng1Complete)) {
+                        specialCaseBA = false;
+
+                    }
+                }
+            }
+
+            // cluster formation
+            if (specialCaseBA) {
+                cluster = vowelBefore + coengBefore + base + vowelAfter + 
robat + shifter1 + coeng1 + coeng2
+                        + shifter2 + vowelBelow + vowelAbove + signAbove + 
signAfter;
+            } else {
+                cluster = vowelBefore + coengBefore + base + robat + shifter1 
+ coeng1 + coeng2 + shifter2
+                        + vowelBelow + vowelAbove + vowelAfter + signAbove + 
signAfter;
+            }
+            result.append(cluster + reserved);
+            state = 0;
+            //end of while
+        }
+
+        return result.toString();
+    }
+}

Propchange: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerRenderer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java?rev=1846994&view=auto
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java
 (added)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java
 Tue Nov 20 12:35:21 2018
@@ -0,0 +1,105 @@
+/*
+ * 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.scripts;
+
+import java.util.List;
+
+import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
+import org.apache.fop.complexscripts.fonts.GlyphTable;
+import org.apache.fop.complexscripts.util.CharAssociation;
+import org.apache.fop.complexscripts.util.GlyphContextTester;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.complexscripts.util.ScriptContextTester;
+import org.apache.fop.fonts.MultiByteFont;
+
+/**
+ * <p>The <code>KhmerScriptProcessor</code> class implements a script 
processor for
+ * performing glyph substitution and positioning operations on content 
associated with the Khmer script.</p>
+ */
+public class KhmerScriptProcessor extends IndicScriptProcessor {
+    private GlyphSequence unprocessedGS;
+    private List associations;
+    private int[] chars;
+
+    KhmerScriptProcessor(String script) {
+        super(script);
+    }
+
+    protected Class<? extends IndicScriptProcessor.DefaultSyllabizer> 
getSyllabizerClass() {
+        return KhmerSyllabizer.class;
+    }
+
+    private static class KhmerSyllabizer extends DefaultSyllabizer {
+        KhmerSyllabizer(String script, String language) {
+            super(script, language);
+        }
+    }
+
+    @Override
+    public GlyphSequence reorderCombiningMarks(GlyphDefinitionTable gdef, 
GlyphSequence glyphSequence,
+                                               int[] unscaledWidths, int[][] 
glyphPositionAdjustments, String script,
+                                               String language) {
+        return glyphSequence;
+    }
+
+    public CharSequence preProcess(CharSequence charSequence, MultiByteFont 
font, List associations) {
+        unprocessedGS = font.charSequenceToGlyphSequence(charSequence, 
associations);
+        return new KhmerRenderer().render(charSequence.toString());
+    }
+
+    public boolean position(GlyphSequence glyphSequence, String script, String 
language, int fontSize,
+                            GlyphTable.UseSpec[] useSpecs, int[] widths, 
int[][] adjustments,
+                            ScriptContextTester scriptContextTester) {
+        glyphSequence.setUnprocessedGS(unprocessedGS);
+        return super.position(glyphSequence, script, language, fontSize, 
useSpecs, widths, adjustments,
+                scriptContextTester);
+    }
+
+    public GlyphSequence substitute(GlyphSequence glyphSequence, String 
script, String language,
+                                    GlyphTable.UseSpec[] useSpecs, 
ScriptContextTester scriptContextTester) {
+        glyphSequence = super.substitute(glyphSequence, script, language, 
useSpecs, scriptContextTester);
+        associations = glyphSequence.getAssociations();
+        chars = glyphSequence.getCharacters().array();
+        return glyphSequence;
+    }
+
+    private ScriptContextTester scriptContextTester = new 
ScriptContextTester() {
+        private GlyphContextTester tester = new GlyphContextTester() {
+            public boolean test(String script, String language, String 
feature, GlyphSequence glyphSequence, int index,
+                                int flags) {
+                CharAssociation charAssociation = (CharAssociation) 
associations.get(index);
+                char vowelSignU = '\u17BB';
+                for (int i = charAssociation.getStart(); i < 
charAssociation.getEnd(); i++) {
+                    if (chars[i] == vowelSignU) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        };
+        public GlyphContextTester getTester(String feature) {
+            return tester;
+        }
+    };
+
+    public ScriptContextTester getPositioningContextTester() {
+        return scriptContextTester;
+    }
+}

Propchange: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/KhmerScriptProcessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
 Tue Nov 20 12:35:21 2018
@@ -31,6 +31,7 @@ import org.apache.fop.complexscripts.fon
 import org.apache.fop.complexscripts.util.CharScript;
 import org.apache.fop.complexscripts.util.GlyphSequence;
 import org.apache.fop.complexscripts.util.ScriptContextTester;
+import org.apache.fop.fonts.MultiByteFont;
 
 // CSOFF: LineLengthCheck
 
@@ -289,4 +290,7 @@ public abstract class ScriptProcessor {
 
     }
 
+    public CharSequence preProcess(CharSequence charSequence, MultiByteFont 
font, List associations) {
+        return charSequence;
+    }
 }

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/CharScript.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/CharScript.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/CharScript.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/CharScript.java
 Tue Nov 20 12:35:21 2018
@@ -802,6 +802,7 @@ public final class CharScript {
         case SCRIPT_TAMIL_2:
         case SCRIPT_TELUGU:
         case SCRIPT_TELUGU_2:
+        case SCRIPT_KHMER:
             return true;
         default:
             return false;

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/GlyphSequence.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/GlyphSequence.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/GlyphSequence.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/complexscripts/util/GlyphSequence.java
 Tue Nov 20 12:35:21 2018
@@ -49,6 +49,8 @@ public class GlyphSequence implements Cl
     /** predications flag */
     private boolean predications;
 
+    protected GlyphSequence unprocessedGS;
+
     /**
      * Instantiate a glyph sequence, reusing (i.e., not copying) the referenced
      * character and glyph buffers and associations. If characters is null, 
then
@@ -74,6 +76,7 @@ public class GlyphSequence implements Cl
         this.glyphs = glyphs;
         this.associations = associations;
         this.predications = predications;
+        unprocessedGS = this;
     }
 
     /**
@@ -98,6 +101,7 @@ public class GlyphSequence implements Cl
      */
     public GlyphSequence(GlyphSequence gs) {
         this (gs.characters.duplicate(), copyBuffer(gs.glyphs), 
copyAssociations(gs.associations), gs.predications);
+        this.unprocessedGS = gs.unprocessedGS;
     }
 
     /**
@@ -181,6 +185,14 @@ public class GlyphSequence implements Cl
         return glyphs.get(index);
     }
 
+    public int getUnprocessedGlyph(int index) throws IndexOutOfBoundsException 
{
+        return unprocessedGS.getGlyph(index);
+    }
+
+    public void setUnprocessedGS(GlyphSequence glyphSequence) {
+        unprocessedGS = glyphSequence;
+    }
+
     /**
      * Set glyph id at specified index.
      * @param index to set glyph

Modified: 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java?rev=1846994&r1=1846993&r2=1846994&view=diff
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java
 (original)
+++ 
xmlgraphics/fop/trunk/fop-core/src/main/java/org/apache/fop/fonts/MultiByteFont.java
 Tue Nov 20 12:35:21 2018
@@ -544,28 +544,32 @@ public class MultiByteFont extends CIDFo
     }
 
     /** {@inheritDoc} */
-    public CharSequence performSubstitution(CharSequence cs, String script, 
String language, List associations,
-                                            boolean retainControls) {
+    public CharSequence performSubstitution(CharSequence charSequence, String 
script, String language,
+                                            List associations, boolean 
retainControls) {
         if (gsub != null) {
-            CharSequence  ncs = normalize(cs, associations);
-            GlyphSequence igs = mapCharsToGlyphs(ncs, associations);
-            GlyphSequence ogs = gsub.substitute(igs, script, language);
+            charSequence = gsub.preProcess(charSequence, script, this, 
associations);
+            GlyphSequence glyphSequence = 
charSequenceToGlyphSequence(charSequence, associations);
+            GlyphSequence glyphSequenceSubstituted = 
gsub.substitute(glyphSequence, script, language);
             if (associations != null) {
                 associations.clear();
-                associations.addAll(ogs.getAssociations());
+                
associations.addAll(glyphSequenceSubstituted.getAssociations());
             }
             if (!retainControls) {
-                ogs = elideControls(ogs);
+                glyphSequenceSubstituted = 
elideControls(glyphSequenceSubstituted);
             }
-            // ocs may not contains all the characters that were in cs.
+            // may not contains all the characters that were in charSequence.
             // see: #createPrivateUseMapping(int gi)
-            CharSequence ocs = mapGlyphsToChars(ogs);
-            return ocs;
+            return mapGlyphsToChars(glyphSequenceSubstituted);
         } else {
-            return cs;
+            return charSequence;
         }
     }
 
+    public GlyphSequence charSequenceToGlyphSequence(CharSequence 
charSequence, List associations) {
+        CharSequence normalizedCharSequence = normalize(charSequence, 
associations);
+        return mapCharsToGlyphs(normalizedCharSequence, associations);
+    }
+
     /** {@inheritDoc} */
     public CharSequence reorderCombiningMarks(
         CharSequence cs, int[][] gpa, String script, String language, List 
associations) {

Added: 
xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java
URL: 
http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java?rev=1846994&view=auto
==============================================================================
--- 
xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java
 (added)
+++ 
xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java
 Tue Nov 20 12:35:21 2018
@@ -0,0 +1,117 @@
+/*
+ * 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.scripts;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.fop.complexscripts.fonts.GlyphCoverageTable;
+import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
+import org.apache.fop.complexscripts.fonts.GlyphSubtable;
+import org.apache.fop.complexscripts.fonts.GlyphTable;
+import org.apache.fop.complexscripts.fonts.OTFLanguage;
+import org.apache.fop.complexscripts.fonts.OTFScript;
+import org.apache.fop.complexscripts.util.CharScript;
+import org.apache.fop.complexscripts.util.GlyphSequence;
+import org.apache.fop.fonts.MultiByteFont;
+
+public class KhmerTestCase {
+    @Test
+    public void testProcessor() {
+        String in = 
"\u179b\u17c1\u1781\u179a\u17c0\u1784\u17b7\u179c\u17d2\u1780\u1780\u1799\u1794\u17d2\u178f\u179a";
+        String out =
+               
"\u17c1\u179b\u1781\u17c1\u179a\u17c0\u1784\u17b7\u179c\u17d2\u1780\u1780\u1799\u1794\u17d2\u178f\u179a";
+        assertEquals(
+                new KhmerScriptProcessor(OTFScript.KHMER).preProcess(in, new 
MultiByteFont(null, null), null), out);
+    }
+
+    @Test
+    public void testPositioning() {
+        GlyphSubtable subtable5 = GlyphPositioningTable.createSubtable(5, 
"lu1", 0, 0, 1,
+                
GlyphCoverageTable.createCoverageTable(Collections.singletonList(0)),
+                Arrays.asList(
+                        
GlyphCoverageTable.createCoverageTable(Collections.singletonList(0)),
+                        0,
+                        1,
+                        new GlyphPositioningTable.MarkAnchor[] {
+                                new GlyphPositioningTable.MarkAnchor(0, new 
GlyphPositioningTable.Anchor(0, 0))
+                        },
+                        new GlyphPositioningTable.Anchor[][][] {
+                                new GlyphPositioningTable.Anchor[][] {
+                                        new GlyphPositioningTable.Anchor[] {
+                                                new 
GlyphPositioningTable.Anchor(12, 0)
+                                        }
+                                }
+                        }
+                ));
+        Map<GlyphTable.LookupSpec, List> lookups = new 
HashMap<GlyphTable.LookupSpec, List>();
+        lookups.put(new GlyphTable.LookupSpec(OTFScript.KHMER, 
OTFLanguage.DEFAULT, "abvm"),
+                Collections.singletonList("lu1"));
+        Map<String, ScriptProcessor> processors = new HashMap<String, 
ScriptProcessor>();
+        processors.put(OTFScript.KHMER, new 
KhmerScriptProcessor(OTFScript.KHMER));
+        GlyphPositioningTable gpt =
+                new GlyphPositioningTable(null, lookups, 
Collections.singletonList(subtable5), processors);
+
+        ScriptProcessor scriptProcessor = 
ScriptProcessor.getInstance(OTFScript.KHMER, processors);
+        MultiByteFont multiByteFont = new MultiByteFont(null, null);
+        GlyphSequence glyphSequence = 
multiByteFont.charSequenceToGlyphSequence("test", null);
+        scriptProcessor.preProcess("test", multiByteFont, null);
+        scriptProcessor.substitute(
+                glyphSequence, OTFScript.KHMER, OTFLanguage.DEFAULT, new 
GlyphTable.UseSpec[0], null);
+        int[][] adjustments = new int[4][1];
+        gpt.position(glyphSequence, OTFScript.KHMER, OTFLanguage.DEFAULT, 0, 
null, adjustments);
+        Assert.assertArrayEquals(adjustments[1], new int[]{12});
+    }
+
+    @Test
+    public void testMakeProcessor() {
+        Assert.assertTrue(IndicScriptProcessor.makeProcessor(OTFScript.KHMER) 
instanceof KhmerScriptProcessor);
+        Assert.assertTrue(CharScript.isIndicScript(OTFScript.KHMER));
+    }
+
+    @Test
+    public void testKhmerRenderer() {
+        KhmerRenderer khmerRenderer = new KhmerRenderer();
+        StringBuilder stringBuilder = new StringBuilder();
+        int khmerStart = 6016;
+        for (int i = khmerStart; i < khmerStart + 128; i++) {
+            stringBuilder.append((char)i);
+        }
+        String allKhmerChars = stringBuilder.toString();
+        String expected = khmerRenderer.render(allKhmerChars);
+        assertEquals(expected.length(), 133);
+
+        StringBuilder diff = new StringBuilder();
+        for (int i = 0; i < allKhmerChars.length(); i++) {
+            if (allKhmerChars.charAt(i) != expected.charAt(i)) {
+                diff.append(expected.charAt(i));
+            }
+        }
+        assertEquals(diff.length(), 66);
+        assertEquals(diff.charAt(0), (char) 6081);
+    }
+}

Propchange: 
xmlgraphics/fop/trunk/fop-core/src/test/java/org/apache/fop/complexscripts/scripts/KhmerTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscr...@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-h...@xmlgraphics.apache.org

Reply via email to