% Copyright 2026 Open-Guji (https://github.com/open-guji) % % Licensed 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. % luatex-cn-gongche.sty % 工尺谱 (Gongche notation) — 主字 + 右侧工尺音字 % % Level 1 (current): plain string of gongche characters on the right side. % Pure wrapper around \SideText, no lua changes. % Level 2 (planned): \音[板]{尺} — per-unit decorations (banyan/octave). % Level 3 (planned): octave/extension/style presets. % \RequirePackage{expl3} \RequirePackage{xparse} \ProvidesExplPackage {decorate/luatex-cn-gongche} {2026/08/07} {0.4.1} {Gongche Notation Support} % ============================================================================ % Key-Value Parameters % ============================================================================ \tl_new:N \l__luatexcn_gongche_scale_tl \tl_new:N \l__luatexcn_gongche_color_tl \tl_new:N \l__luatexcn_gongche_offset_tl \keys_define:nn { luatexcn / gongche } { scale .tl_set:N = \l__luatexcn_gongche_scale_tl, scale .initial:n = 0.5, color .tl_set:N = \l__luatexcn_gongche_color_tl, color .initial:n = black, offset .tl_set:N = \l__luatexcn_gongche_offset_tl, offset .initial:n = 0pt, % CJK aliases 缩放 .tl_set:N = \l__luatexcn_gongche_scale_tl, 縮放 .tl_set:N = \l__luatexcn_gongche_scale_tl, 颜色 .tl_set:N = \l__luatexcn_gongche_color_tl, 顏色 .tl_set:N = \l__luatexcn_gongche_color_tl, 偏移 .tl_set:N = \l__luatexcn_gongche_offset_tl, } \NewDocumentCommand{\gongcheSetup}{ m } { \keys_set:nn { luatexcn / gongche } { #1 } } % ============================================================================ % \GongChe (Level 1) — main char + right-side gongche string % Syntax: % \工尺{主字}{工尺字串} basic % \工尺[颜色=red, 缩放=0.4]{主字}{尺工六} per-call options % Currently delegates to \SideText (右侧旁注). Level 2 will replace the % right-content path with a unit-list data model that supports per-音 板眼. % ============================================================================ \NewDocumentCommand{\GongChe}{ O{} m m } { \group_begin: \keys_set:nn { luatexcn / gongche } { #1 } % Push gongche-specific defaults into the sidetext namespace within % this group. Outer \sideTextSetup remains unchanged after group ends. \keys_set:nn { luatexcn / sidetext } { scale = \l__luatexcn_gongche_scale_tl, color = \l__luatexcn_gongche_color_tl, offset = \l__luatexcn_gongche_offset_tl, } \SideText { #2 } { #3 } \group_end: } % CJK aliases \NewCommandCopy{\工尺}{\GongChe} \NewCommandCopy{\工尺设置}{\gongcheSetup} \NewCommandCopy{\工尺設置}{\gongcheSetup} % ============================================================================ % \音[]{字} (Level 2/3) — gongche char + orthogonal markers % % Two independent marker slots, both can be active in one \音 call: % - rhythm slot: 板眼 marks (板/眼/头板/底板/底叮) % - octave slot: 八度 marks (高/低/昆低) % % Syntax: % \音{尺} bare char, no marks % \音[板]{尺} rhythm only (red 。) % \音[高]{尺} octave only (亻 prefix) % \音[板, 高]{尺} rhythm + octave (both markers) % \音[板, 颜色=blue]{尺} rhythm color override % \音[高, 八度颜色=red]{尺} octave color override % \音[字符=●, 缩放=0.3, 颜色=green]{尺} manual rhythm % % Sign convention (per decorate.lua): xshift>0 = visual LEFT (toward main), % yshift>0 = visual DOWN. % Offsets are resolved against base font em; render multiplies by gongche % scale so 0.5em is "relative to the 工尺 char's visual size". % ============================================================================ % --- Rhythm slot (板眼) --- \tl_new:N \l__luatexcn_yin_rhythm_char_tl \tl_new:N \l__luatexcn_yin_rhythm_xshift_tl \tl_new:N \l__luatexcn_yin_rhythm_yshift_tl \tl_new:N \l__luatexcn_yin_rhythm_scale_tl \tl_new:N \l__luatexcn_yin_rhythm_color_tl % --- Octave slot (八度) --- \tl_new:N \l__luatexcn_yin_octave_char_tl \tl_new:N \l__luatexcn_yin_octave_xshift_tl \tl_new:N \l__luatexcn_yin_octave_yshift_tl \tl_new:N \l__luatexcn_yin_octave_scale_tl \tl_new:N \l__luatexcn_yin_octave_color_tl \keys_define:nn { luatexcn / yin } { % --- Rhythm slot manual setters --- rhythm-char .tl_set:N = \l__luatexcn_yin_rhythm_char_tl, rhythm-xshift .tl_set:N = \l__luatexcn_yin_rhythm_xshift_tl, rhythm-yshift .tl_set:N = \l__luatexcn_yin_rhythm_yshift_tl, rhythm-scale .tl_set:N = \l__luatexcn_yin_rhythm_scale_tl, rhythm-color .tl_set:N = \l__luatexcn_yin_rhythm_color_tl, % CJK aliases for rhythm (most common; bare 颜色/缩放/字符 affect rhythm) 字符 .tl_set:N = \l__luatexcn_yin_rhythm_char_tl, 横偏 .tl_set:N = \l__luatexcn_yin_rhythm_xshift_tl, 橫偏 .tl_set:N = \l__luatexcn_yin_rhythm_xshift_tl, 纵偏 .tl_set:N = \l__luatexcn_yin_rhythm_yshift_tl, 縱偏 .tl_set:N = \l__luatexcn_yin_rhythm_yshift_tl, 缩放 .tl_set:N = \l__luatexcn_yin_rhythm_scale_tl, 縮放 .tl_set:N = \l__luatexcn_yin_rhythm_scale_tl, 颜色 .tl_set:N = \l__luatexcn_yin_rhythm_color_tl, 顏色 .tl_set:N = \l__luatexcn_yin_rhythm_color_tl, % --- Octave slot manual setters --- octave-char .tl_set:N = \l__luatexcn_yin_octave_char_tl, octave-xshift .tl_set:N = \l__luatexcn_yin_octave_xshift_tl, octave-yshift .tl_set:N = \l__luatexcn_yin_octave_yshift_tl, octave-scale .tl_set:N = \l__luatexcn_yin_octave_scale_tl, octave-color .tl_set:N = \l__luatexcn_yin_octave_color_tl, % CJK aliases for octave overrides 八度字符 .tl_set:N = \l__luatexcn_yin_octave_char_tl, 八度颜色 .tl_set:N = \l__luatexcn_yin_octave_color_tl, 八度顏色 .tl_set:N = \l__luatexcn_yin_octave_color_tl, 八度缩放 .tl_set:N = \l__luatexcn_yin_octave_scale_tl, 八度縮放 .tl_set:N = \l__luatexcn_yin_octave_scale_tl, % --- Rhythm presets (板眼) --- 板 .meta:n = { rhythm-char = 。, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.35, rhythm-color = red }, 眼 .meta:n = { rhythm-char = 、, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.35, rhythm-color = red }, 头板 .meta:n = { rhythm-char = ×, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.45, rhythm-color = red }, 頭板 .meta:n = { rhythm-char = ×, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.45, rhythm-color = red }, 底板 .meta:n = { rhythm-char = └, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.45, rhythm-color = red }, 底叮 .meta:n = { rhythm-char = ⌐, rhythm-xshift = -0.5em, rhythm-yshift = 0em, rhythm-scale = 0.45, rhythm-color = red }, % --- Octave presets (八度) --- % 高 = 闽剧/粤剧高八度 — 亻 prefix above the gongche char 高 .meta:n = { octave-char = 亻, octave-xshift = 0em, octave-yshift = -0.85em, octave-scale = 0.7, octave-color = black }, % 低 = 粤剧低八度 — 彳 prefix above 低 .meta:n = { octave-char = 彳, octave-xshift = 0em, octave-yshift = -0.85em, octave-scale = 0.7, octave-color = black }, % 昆低 = 昆曲低八度 — 钩 below the gongche char (right side) 昆低 .meta:n = { octave-char = ⌒, octave-xshift = -0.5em, octave-yshift = 0.4em, octave-scale = 0.6, octave-color = black }, } % Helper: emit one decoration marker via register_decorate. % Parameters are TL variables (passed by name); \lua_now:e e-expands them. \cs_new_protected:Npn \__luatexcn_yin_emit:NNNNN #1 #2 #3 #4 #5 { \lua_now:e { local~constants~=~require('core.luatex-cn-constants') constants.register_decorate( "\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}", nil, "\luaescapestring{#5}", font.current(), "\luaescapestring{#4}" ) } \box0\scan_stop: } \NewDocumentCommand{\Yin}{ O{} m } { \group_begin: % Reset slot chars (no marker emitted unless a preset/setter activates). % Geometry defaults stay non-empty so manual char overrides (e.g., % \音[字符=●]{尺}) inherit a sensible position. \tl_clear:N \l__luatexcn_yin_rhythm_char_tl \tl_set:Nn \l__luatexcn_yin_rhythm_xshift_tl { -0.5em } \tl_set:Nn \l__luatexcn_yin_rhythm_yshift_tl { 0em } \tl_set:Nn \l__luatexcn_yin_rhythm_scale_tl { 0.35 } \tl_set:Nn \l__luatexcn_yin_rhythm_color_tl { red } \tl_clear:N \l__luatexcn_yin_octave_char_tl \tl_set:Nn \l__luatexcn_yin_octave_xshift_tl { 0em } \tl_set:Nn \l__luatexcn_yin_octave_yshift_tl { -0.85em } \tl_set:Nn \l__luatexcn_yin_octave_scale_tl { 0.7 } \tl_set:Nn \l__luatexcn_yin_octave_color_tl { black } \keys_set:nn { luatexcn / yin } { #1 } #2 \tl_if_empty:NF \l__luatexcn_yin_rhythm_char_tl { \__luatexcn_yin_emit:NNNNN \l__luatexcn_yin_rhythm_char_tl \l__luatexcn_yin_rhythm_xshift_tl \l__luatexcn_yin_rhythm_yshift_tl \l__luatexcn_yin_rhythm_scale_tl \l__luatexcn_yin_rhythm_color_tl } \tl_if_empty:NF \l__luatexcn_yin_octave_char_tl { \__luatexcn_yin_emit:NNNNN \l__luatexcn_yin_octave_char_tl \l__luatexcn_yin_octave_xshift_tl \l__luatexcn_yin_octave_yshift_tl \l__luatexcn_yin_octave_scale_tl \l__luatexcn_yin_octave_color_tl } \group_end: } \NewCommandCopy{\音}{\Yin} % ============================================================================ % \拖音[] — duration extension stroke (filler unit) % Uses 丨 (U+4E28 CJK STROKE) as the gongche char of a continuation unit. % Combines naturally with rhythm slots so each beat in a held note can be % marked appropriately. % % Example — 4-beat 尺 (attack on board, then 3 continuation beats): % \工尺{春}{\音[板]{尺}\拖音[眼]\拖音[板]\拖音[眼]} % % Bare \拖音 = no rhythm marker (just the stroke). % ============================================================================ \NewDocumentCommand{\Drag}{ O{} } { \Yin [ #1 ] { 丨 } } \NewCommandCopy{\拖音}{\Drag} % ============================================================================ % 句读 / 宫调 — 小字注释 (sentence/rhyme markers, tune annotations) % % 在工尺谱中常需要标注: % - 句 (sentence end), 韵 (rhyme) — 短小句读符号 % - 宫调名 (仙吕调/南吕调/...), 别名 (一名XX) — 标题旁的小字注 % % 这些都是约半个主字大小的灰度文字, 与正文同流但视觉上区分. % \句读字{X} 是底层命令; \句 \韵 \宫调 是语义糖. % ============================================================================ \NewDocumentCommand{\句读字}{ m } { {\small #1} } \NewDocumentCommand{\句}{} { \句读字{句} } \NewDocumentCommand{\韵}{} { \句读字{韻} } \NewDocumentCommand{\韻}{} { \句读字{韻} } \NewDocumentCommand{\宫调}{ m } { \句读字{#1} } \NewDocumentCommand{\宮調}{ m } { \句读字{#1} } \NewDocumentCommand{\曲牌}{ m } { \句读字{#1} } \NewDocumentCommand{\调名注}{ m } { \句读字{#1} } \NewDocumentCommand{\調名注}{ m } { \句读字{#1} } \ExplSyntaxOff% % \endinput%