📄 break.c
字号:
/* Pango * break.c: * * Copyright (C) 1999 Red Hat Software * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#include <config.h>#include "pango-break.h"#include "pango-modules.h"#include <string.h>#define PARAGRAPH_SEPARATOR 0x2029#define PARAGRAPH_SEPARATOR_STRING "\xE2\x80\xA9"/* See http://www.unicode.org/unicode/reports/tr14/ if you hope * to understand the line breaking code. */typedef enum{ BREAK_ALREADY_HANDLED, /* didn't use the table */ BREAK_PROHIBITED, /* no break, even if spaces intervene */ BREAK_IF_SPACES, /* "indirect break" (only if there are spaces) */ BREAK_ALLOWED /* "direct break" (can always break here) */ /* TR 14 has one more break-opportunity class, * "indirect break opportunity for combining marks following a space" * but we handle that inline in the code. */} BreakOpportunity;enum{ INDEX_OPEN_PUNCTUATION, INDEX_CLOSE_PUNCTUATION, INDEX_QUOTATION, INDEX_NON_BREAKING_GLUE, INDEX_NON_STARTER, INDEX_EXCLAMATION, INDEX_SYMBOL, INDEX_INFIX_SEPARATOR, INDEX_PREFIX, INDEX_POSTFIX, INDEX_NUMERIC, INDEX_ALPHABETIC, INDEX_IDEOGRAPHIC, INDEX_INSEPARABLE, INDEX_HYPHEN, INDEX_AFTER, INDEX_BEFORE, INDEX_BEFORE_AND_AFTER, INDEX_ZERO_WIDTH_SPACE, INDEX_COMBINING_MARK, INDEX_WORD_JOINER, /* End of the table */ INDEX_END_OF_TABLE, /* The following are not in the tables */ INDEX_MANDATORY, INDEX_CARRIAGE_RETURN, INDEX_LINE_FEED, INDEX_SURROGATE, INDEX_CONTINGENT, INDEX_SPACE, INDEX_COMPLEX_CONTEXT, INDEX_AMBIGUOUS, INDEX_UNKNOWN, INDEX_NEXT_LINE, INDEX_HANGUL_L_JAMO, INDEX_HANGUL_V_JAMO, INDEX_HANGUL_T_JAMO, INDEX_HANGUL_LV_SYLLABLE, INDEX_HANGUL_LVT_SYLLABLE,};static const BreakOpportunity row_OPEN_PUNCTUATION[INDEX_END_OF_TABLE] = { BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_CLOSE_PUNCTUATION[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_QUOTATION[INDEX_END_OF_TABLE] = { BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_NON_BREAKING_GLUE[INDEX_END_OF_TABLE] = { BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_NON_STARTER[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_EXCLAMATION[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_SYMBOL[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_INFIX_SEPARATOR[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_PREFIX[INDEX_END_OF_TABLE] = { BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_POSTFIX[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_NUMERIC[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_ALPHABETIC[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_IDEOGRAPHIC[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_INSEPARABLE[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_HYPHEN[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_AFTER[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_BEFORE[INDEX_END_OF_TABLE] = { BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_BEFORE_AND_AFTER[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_ZERO_WIDTH_SPACE[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED};static const BreakOpportunity row_COMBINING_MARK[INDEX_END_OF_TABLE] = { BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_ALLOWED, BREAK_ALLOWED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity row_WORD_JOINER[INDEX_END_OF_TABLE] = { BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_IF_SPACES, BREAK_PROHIBITED, BREAK_PROHIBITED, BREAK_PROHIBITED};static const BreakOpportunity *const line_break_rows[INDEX_END_OF_TABLE] = { row_OPEN_PUNCTUATION, /* INDEX_OPEN_PUNCTUATION */ row_CLOSE_PUNCTUATION, /* INDEX_CLOSE_PUNCTUATION */ row_QUOTATION, /* INDEX_QUOTATION */ row_NON_BREAKING_GLUE, /* INDEX_NON_BREAKING_GLUE */ row_NON_STARTER, /* INDEX_NON_STARTER */ row_EXCLAMATION, /* INDEX_EXCLAMATION */ row_SYMBOL, /* INDEX_SYMBOL */ row_INFIX_SEPARATOR, /* INDEX_INFIX_SEPARATOR */ row_PREFIX, /* INDEX_PREFIX */ row_POSTFIX, /* INDEX_POSTFIX */ row_NUMERIC, /* INDEX_NUMERIC */ row_ALPHABETIC, /* INDEX_ALPHABETIC */ row_IDEOGRAPHIC, /* INDEX_IDEOGRAPHIC */ row_INSEPARABLE, /* INDEX_INSEPARABLE */ row_HYPHEN, /* INDEX_HYPHEN */ row_AFTER, /* INDEX_AFTER */ row_BEFORE, /* INDEX_BEFORE */ row_BEFORE_AND_AFTER, /* INDEX_BEFORE_AND_AFTER */ row_ZERO_WIDTH_SPACE, /* INDEX_ZERO_WIDTH_SPACE */ row_COMBINING_MARK, /* INDEX_COMBINING_MARK */ row_WORD_JOINER /* INDEX_WORD_JOINER */};/* Map GUnicodeBreakType to table indexes */static const int line_break_indexes[] = { INDEX_MANDATORY, INDEX_CARRIAGE_RETURN, INDEX_LINE_FEED, INDEX_COMBINING_MARK, INDEX_SURROGATE, INDEX_ZERO_WIDTH_SPACE, INDEX_INSEPARABLE, INDEX_NON_BREAKING_GLUE, INDEX_CONTINGENT, INDEX_SPACE, INDEX_AFTER, INDEX_BEFORE, INDEX_BEFORE_AND_AFTER, INDEX_HYPHEN, INDEX_NON_STARTER, INDEX_OPEN_PUNCTUATION, INDEX_CLOSE_PUNCTUATION, INDEX_QUOTATION, INDEX_EXCLAMATION, INDEX_IDEOGRAPHIC, INDEX_NUMERIC, INDEX_INFIX_SEPARATOR, INDEX_SYMBOL, INDEX_ALPHABETIC, INDEX_PREFIX, INDEX_POSTFIX, INDEX_COMPLEX_CONTEXT, INDEX_AMBIGUOUS, INDEX_UNKNOWN, INDEX_NEXT_LINE, INDEX_WORD_JOINER, INDEX_HANGUL_L_JAMO, INDEX_HANGUL_V_JAMO, INDEX_HANGUL_T_JAMO, INDEX_HANGUL_LV_SYLLABLE, INDEX_HANGUL_LVT_SYLLABLE};#define BREAK_TYPE_SAFE(btype) \ (btype < G_N_ELEMENTS(line_break_indexes) ? btype : G_UNICODE_BREAK_UNKNOWN)#define BREAK_INDEX(btype) \ (line_break_indexes[(btype)])#define BREAK_ROW(before_type) \ (line_break_rows[BREAK_INDEX (before_type)])#define BREAK_OP(before_type, after_type) \ (BREAK_ROW (before_type)[BREAK_INDEX (after_type)])#define IN_BREAK_TABLE(btype) \ (btype < G_N_ELEMENTS(line_break_indexes) && BREAK_INDEX(btype) < INDEX_END_OF_TABLE)/* * Hangul Conjoining Jamo handling. * * The way we implement it is just a bit different from TR14, * but produces the same results. * The same algorithm is also used in TR29 for cluster boundaries. * *//* An enum that works as the states of the Hangul syllables system. **/typedef enum{ JAMO_L, /* G_UNICODE_BREAK_HANGUL_L_JAMO */ JAMO_V, /* G_UNICODE_BREAK_HANGUL_V_JAMO */ JAMO_T, /* G_UNICODE_BREAK_HANGUL_T_JAMO */ JAMO_LV, /* G_UNICODE_BREAK_HANGUL_LV_SYLLABLE */ JAMO_LVT, /* G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE */ NO_JAMO /* Other */} JamoType;/* There are Hangul syllables encoded as characters, that act like a * sequence of Jamos. For each character we define a JamoType * that the character starts with, and one that it ends with. This * decomposes JAMO_LV and JAMO_LVT to simple other JAMOs. So for * example, a character with LineBreak type * G_UNICODE_BREAK_HANGUL_LV_SYLLABLE has start=JAMO_L and end=JAMO_V. */typedef struct _CharJamoProps{ JamoType start, end;} CharJamoProps;/* Map from JamoType to CharJamoProps that hold only simple * JamoTypes (no LV or LVT) or none. */static const CharJamoProps HangulJamoProps[] = { {JAMO_L, JAMO_L}, /* JAMO_L */ {JAMO_V, JAMO_V}, /* JAMO_V */ {JAMO_T, JAMO_T}, /* JAMO_T */ {JAMO_L, JAMO_V}, /* JAMO_LV */ {JAMO_L, JAMO_T}, /* JAMO_LVT */ {NO_JAMO, NO_JAMO} /* NO_JAMO */};/* A character forms a syllable with the previous character if and only if: * JamoType(this) is not NO_JAMO and: * * HangulJamoProps[JamoType(prev)].end and * HangulJamoProps[JamoType(this)].start are equal, * or the former is one less than the latter. */#define IS_JAMO(btype) \ ((btype >= G_UNICODE_BREAK_HANGUL_L_JAMO) && \ (btype <= G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE))#define JAMO_TYPE(btype) \ (IS_JAMO(btype) ? (btype - G_UNICODE_BREAK_HANGUL_L_JAMO) : NO_JAMO)/* "virama script" is just an optimization; it includes a bunch of * scripts without viramas in them */#define VIRAMA_SCRIPT(wc) ((wc) >= 0x0901 && (wc) <= 0x17FF)#define VIRAMA(wc) ((wc) == 0x094D || \ (wc) == 0x09CD || \ (wc) == 0x0A4D || \ (wc) == 0x0ACD || \ (wc) == 0x0B4D || \ (wc) == 0x0BCD || \ (wc) == 0x0C4D || \ (wc) == 0x0CCD || \ (wc) == 0x0D4D || \ (wc) == 0x0DCA || \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -