Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxScriptItemizer.cpp
blob29a2b4d0ed00e3cef8f4831fa9683d4e663f590d
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * This file is based on usc_impl.c from ICU 4.2.0.1, slightly adapted
8 * for use within Mozilla Gecko, separate from a standard ICU build.
10 * The original ICU license of the code follows:
12 * ICU License - ICU 1.8.1 and later
14 * COPYRIGHT AND PERMISSION NOTICE
16 * Copyright (c) 1995-2009 International Business Machines Corporation and
17 * others
19 * All rights reserved.
21 * Permission is hereby granted, free of charge, to any person obtaining a
22 * copy of this software and associated documentation files (the "Software"),
23 * to deal in the Software without restriction, including without limitation
24 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
25 * copies of the Software, and to permit persons to whom the Software is
26 * furnished to do so, provided that the above copyright notice(s) and this
27 * permission notice appear in all copies of the Software and that both the
28 * above copyright notice(s) and this permission notice appear in supporting
29 * documentation.
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
34 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
35 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
36 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
37 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
38 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
39 * SOFTWARE.
41 * Except as contained in this notice, the name of a copyright holder shall
42 * not be used in advertising or otherwise to promote the sale, use or other
43 * dealings in this Software without prior written authorization of the
44 * copyright holder.
46 * All trademarks and registered trademarks mentioned herein are the property
47 * of their respective owners.
50 #include "gfxScriptItemizer.h"
51 #include "mozilla/intl/UnicodeProperties.h"
52 #include "nsCharTraits.h"
53 #include "nsUnicodeProperties.h"
54 #include "harfbuzz/hb.h"
56 using namespace mozilla::intl;
57 using namespace mozilla::unicode;
59 #define MOD(sp) ((sp) % PAREN_STACK_DEPTH)
60 #define LIMIT_INC(sp) \
61 (((sp) < PAREN_STACK_DEPTH) ? (sp) + 1 : PAREN_STACK_DEPTH)
62 #define INC(sp, count) (MOD((sp) + (count)))
63 #define INC1(sp) (INC(sp, 1))
64 #define DEC(sp, count) (MOD((sp) + PAREN_STACK_DEPTH - (count)))
65 #define DEC1(sp) (DEC(sp, 1))
66 #define STACK_IS_EMPTY() (pushCount <= 0)
67 #define STACK_IS_NOT_EMPTY() (!STACK_IS_EMPTY())
68 #define TOP() (parenStack[parenSP])
69 #define SYNC_FIXUP() (fixupCount = 0)
71 void gfxScriptItemizer::push(uint32_t endPairChar, Script newScriptCode) {
72 pushCount = LIMIT_INC(pushCount);
73 fixupCount = LIMIT_INC(fixupCount);
75 parenSP = INC1(parenSP);
76 parenStack[parenSP].endPairChar = endPairChar;
77 parenStack[parenSP].scriptCode = newScriptCode;
80 void gfxScriptItemizer::pop() {
81 if (STACK_IS_EMPTY()) {
82 return;
85 if (fixupCount > 0) {
86 fixupCount -= 1;
89 pushCount -= 1;
90 parenSP = DEC1(parenSP);
92 /* If the stack is now empty, reset the stack
93 pointers to their initial values.
95 if (STACK_IS_EMPTY()) {
96 parenSP = -1;
100 void gfxScriptItemizer::fixup(Script newScriptCode) {
101 int32_t fixupSP = DEC(parenSP, fixupCount);
103 while (fixupCount-- > 0) {
104 fixupSP = INC1(fixupSP);
105 parenStack[fixupSP].scriptCode = newScriptCode;
109 static inline bool CanMergeWithContext(Script aScript) {
110 return aScript <= Script::INHERITED || aScript == Script::UNKNOWN;
113 // We regard the current char as having the same script as the in-progress run
114 // if either script is Common/Inherited/Unknown, or if the run script appears
115 // in the character's ScriptExtensions, or if the char is a cluster extender.
116 static inline bool SameScript(Script runScript, Script currCharScript,
117 uint32_t aCurrCh) {
118 return CanMergeWithContext(runScript) ||
119 CanMergeWithContext(currCharScript) || currCharScript == runScript ||
120 IsClusterExtender(aCurrCh) ||
121 UnicodeProperties::HasScript(aCurrCh, runScript);
124 gfxScriptItemizer::gfxScriptItemizer(const char16_t* src, uint32_t length)
125 : textPtr(src), textLength(length) {
126 reset();
129 void gfxScriptItemizer::SetText(const char16_t* src, uint32_t length) {
130 textPtr = src;
131 textLength = length;
133 reset();
136 bool gfxScriptItemizer::Next(uint32_t& aRunStart, uint32_t& aRunLimit,
137 Script& aRunScript) {
138 /* if we've fallen off the end of the text, we're done */
139 if (scriptLimit >= textLength) {
140 return false;
143 SYNC_FIXUP();
144 scriptCode = Script::COMMON;
145 Script fallbackScript = Script::UNKNOWN;
147 for (scriptStart = scriptLimit; scriptLimit < textLength; scriptLimit += 1) {
148 uint32_t ch;
149 Script sc;
150 uint32_t startOfChar = scriptLimit;
152 ch = textPtr[scriptLimit];
154 /* decode UTF-16 (may be surrogate pair) */
155 if (NS_IS_HIGH_SURROGATE(ch) && scriptLimit < textLength - 1) {
156 uint32_t low = textPtr[scriptLimit + 1];
157 if (NS_IS_LOW_SURROGATE(low)) {
158 ch = SURROGATE_TO_UCS4(ch, low);
159 scriptLimit += 1;
163 // Initialize gc to UNASSIGNED; we'll only set it to the true GC
164 // if the character has script=COMMON, otherwise we don't care.
165 uint8_t gc = HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
167 sc = UnicodeProperties::GetScriptCode(ch);
168 if (sc == Script::COMMON) {
170 * Paired character handling:
172 * if it's an open character, push it onto the stack.
173 * if it's a close character, find the matching open on the
174 * stack, and use that script code. Any non-matching open
175 * characters above it on the stack will be popped.
177 * We only do this if the script is COMMON; for chars with
178 * specific script assignments, we just use them as-is.
180 gc = GetGeneralCategory(ch);
181 if (gc == HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION) {
182 uint32_t endPairChar = UnicodeProperties::CharMirror(ch);
183 if (endPairChar != ch) {
184 push(endPairChar, scriptCode);
186 } else if (gc == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION &&
187 UnicodeProperties::IsMirrored(ch)) {
188 while (STACK_IS_NOT_EMPTY() && TOP().endPairChar != ch) {
189 pop();
192 if (STACK_IS_NOT_EMPTY()) {
193 sc = TOP().scriptCode;
198 // Both Hiragana and Katakana are shaped as OpenType 'kana'. Merge them
199 // here to avoid script-run breaks and allow kerning to apply between the
200 // two alphabets.
201 if (sc == Script::HIRAGANA) {
202 sc = Script::KATAKANA;
205 if (SameScript(scriptCode, sc, ch)) {
206 if (scriptCode == Script::COMMON) {
207 // If we have not yet resolved a specific scriptCode for the run,
208 // check whether this character provides it.
209 if (!CanMergeWithContext(sc)) {
210 // Use this character's script.
211 scriptCode = sc;
212 fixup(scriptCode);
213 } else if (fallbackScript == Script::UNKNOWN) {
214 // See if the character has a ScriptExtensions property we can
215 // store for use in the event the run remains unresolved.
216 UnicodeProperties::ScriptExtensionVector extensions;
217 auto extResult = UnicodeProperties::GetExtensions(ch, extensions);
218 if (extResult.isOk()) {
219 Script ext = Script(extensions[0]);
220 if (!CanMergeWithContext(ext)) {
221 fallbackScript = ext;
228 * if this character is a close paired character,
229 * pop the matching open character from the stack
231 if (gc == HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION &&
232 UnicodeProperties::IsMirrored(ch)) {
233 pop();
235 } else {
237 * reset scriptLimit in case it was advanced during reading a
238 * multiple-code-unit character
240 scriptLimit = startOfChar;
242 break;
246 aRunStart = scriptStart;
247 aRunLimit = scriptLimit;
249 if (scriptCode == Script::COMMON && fallbackScript != Script::UNKNOWN) {
250 aRunScript = fallbackScript;
251 } else {
252 aRunScript = scriptCode;
255 return true;