1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp.  and others 1998-2013 - All Rights Reserved
29 *
30 */
31
32#include "LETypes.h"
33#include "MorphTables.h"
34#include "StateTables.h"
35#include "MorphStateTables.h"
36#include "SubtableProcessor2.h"
37#include "StateTableProcessor2.h"
38#include "ContextualGlyphInsertionProc2.h"
39#include "LEGlyphStorage.h"
40#include "LESwaps.h"
41
42U_NAMESPACE_BEGIN
43
44UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ContextualGlyphInsertionProcessor2)
45
46ContextualGlyphInsertionProcessor2::ContextualGlyphInsertionProcessor2(
47         const LEReferenceTo<MorphSubtableHeader2> &morphSubtableHeader, LEErrorCode &success)
48  : StateTableProcessor2(morphSubtableHeader, success)
49{
50  contextualGlyphHeader = LEReferenceTo<ContextualGlyphInsertionHeader2>(morphSubtableHeader, success);
51  if(LE_FAILURE(success) || !contextualGlyphHeader.isValid()) return;
52  le_uint32 insertionTableOffset = SWAPL(contextualGlyphHeader->insertionTableOffset);
53  insertionTable = LEReferenceToArrayOf<le_uint16>(stHeader, success, insertionTableOffset, LE_UNBOUNDED_ARRAY);
54  entryTable = LEReferenceToArrayOf<ContextualGlyphInsertionStateEntry2>(stHeader, success, entryTableOffset, LE_UNBOUNDED_ARRAY);
55}
56
57ContextualGlyphInsertionProcessor2::~ContextualGlyphInsertionProcessor2()
58{
59}
60
61void ContextualGlyphInsertionProcessor2::beginStateTable()
62{
63    markGlyph = 0;
64}
65
66void ContextualGlyphInsertionProcessor2::doInsertion(LEGlyphStorage &glyphStorage,
67                                                     le_int16 atGlyph,
68                                                     le_int16 &index,
69                                                     le_int16 count,
70                                                     le_bool /* isKashidaLike */,
71                                                     le_bool isBefore,
72                                                     LEErrorCode &success) {
73  LEGlyphID *insertGlyphs = glyphStorage.insertGlyphs(atGlyph, count + 1, success);
74
75  if(LE_FAILURE(success) || insertGlyphs==NULL) {
76    return;
77  }
78
79  // Note: Kashida vs Split Vowel seems to only affect selection and highlighting.
80  // We note the flag, but do not layout different.
81  // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6mort.html
82
83  le_int16 targetIndex = 0;
84  if(isBefore) {
85    // insert at beginning
86    insertGlyphs[targetIndex++] = glyphStorage[atGlyph];
87  } else {
88    // insert at end
89    insertGlyphs[count] = glyphStorage[atGlyph];
90  }
91
92  while(count--) {
93    insertGlyphs[targetIndex++] = insertionTable.getObject(index++, success);
94  }
95  glyphStorage.applyInsertions();
96}
97
98le_uint16 ContextualGlyphInsertionProcessor2::processStateEntry(LEGlyphStorage &glyphStorage, le_int32 &currGlyph,
99                                                                EntryTableIndex2 index, LEErrorCode &success)
100{
101    const ContextualGlyphInsertionStateEntry2 *entry = entryTable.getAlias(index, success);
102
103    if(LE_FAILURE(success)) return 0; // TODO- which state?
104
105    le_uint16 newState = SWAPW(entry->newStateIndex);
106    le_uint16 flags = SWAPW(entry->flags);
107
108    le_int16 markIndex = SWAPW(entry->markedInsertionListIndex);
109    if (markIndex > 0) {
110        if (markGlyph < 0 || markGlyph >= glyphStorage.getGlyphCount()) {
111           success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
112           return 0;
113        }
114        le_int16 count = (flags & cgiMarkedInsertCountMask) >> 5;
115        le_bool isKashidaLike = (flags & cgiMarkedIsKashidaLike);
116        le_bool isBefore = (flags & cgiMarkInsertBefore);
117        doInsertion(glyphStorage, markGlyph, markIndex, count, isKashidaLike, isBefore, success);
118    }
119
120    le_int16 currIndex = SWAPW(entry->currentInsertionListIndex);
121    if (currIndex > 0) {
122        if (currGlyph < 0 || currGlyph >= glyphStorage.getGlyphCount()) {
123           success = LE_INDEX_OUT_OF_BOUNDS_ERROR;
124           return 0;
125        }
126        le_int16 count = flags & cgiCurrentInsertCountMask;
127        le_bool isKashidaLike = (flags & cgiCurrentIsKashidaLike);
128        le_bool isBefore = (flags & cgiCurrentInsertBefore);
129        doInsertion(glyphStorage, currGlyph, currIndex, count, isKashidaLike, isBefore, success);
130    }
131
132    if (flags & cgiSetMark) {
133        markGlyph = currGlyph;
134    }
135
136    if (!(flags & cgiDontAdvance)) {
137        currGlyph += dir;
138    }
139
140    return newState;
141}
142
143void ContextualGlyphInsertionProcessor2::endStateTable()
144{
145}
146
147U_NAMESPACE_END
148