From 716060b099cb29170215ce8c03885d09a1d1446a Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:47:31 +0900 Subject: bluebey-studio: 縦書きの括弧「」『』[]()などを90度回転に対応 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 縦書きでは開き括弧が下向き・閉じ括弧が上向きになるよう、括弧を90度回転(枠内は中央のまま)。canvasプレビューとSVG書き出しの両方。 --- public/bluebey-studio/src/caption.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'public/bluebey-studio/src') diff --git a/public/bluebey-studio/src/caption.js b/public/bluebey-studio/src/caption.js index df9c952..71759ea 100644 --- a/public/bluebey-studio/src/caption.js +++ b/public/bluebey-studio/src/caption.js @@ -101,21 +101,34 @@ const VERTICAL_OFFSETS = new Map(); for (const ch of SMALL_KANA) VERTICAL_OFFSETS.set(ch, { dx: 0.12, dy: -0.12 }); // 縦書きの句読点は、横書きの左下ではなく、字枠の右上に置く。 for (const ch of HANGING_PUNCTUATION) VERTICAL_OFFSETS.set(ch, { dx: 0.68, dy: -0.55 }); -for (const ch of '「『(〔') VERTICAL_OFFSETS.set(ch, { dx: 0.14, dy: -0.12 }); -for (const ch of '」』)〕') VERTICAL_OFFSETS.set(ch, { dx: -0.14, dy: 0.12 }); /** * Characters drawn turned a quarter turn when stacked, so they read the way - * 縦書き sets them: the prolonged sound mark `ー` becomes an upright bar. + * 縦書き sets them: the prolonged sound mark `ー` becomes an upright bar, and the + * brackets `「」『』[]()` turn so they open down the column. */ const VERTICAL_ROTATED = new Set(['ー', '~', '〜', '―']); +const VERTICAL_BRACKETS = new Set([ + '「', '」', '『', '』', '(', ')', '[', ']', '【', '】', '〔', '〕', '〈', '〉', '《', '》', +]); /** * How far a rotated glyph is nudged down inside its cell, in em. The rotated `ー` - * is centred otherwise, which leaves it hugging the character above it. + * is centred otherwise, which leaves it hugging the character above it; the + * brackets stay centred. */ const VERTICAL_ROTATED_DROP = 0.14; +/** + * The downward nudge (em) for a glyph that is turned a quarter turn in 縦書き, or + * `null` when the glyph is drawn the ordinary way. + */ +function verticalRotation(ch) { + if (VERTICAL_ROTATED.has(ch)) return VERTICAL_ROTATED_DROP; + if (VERTICAL_BRACKETS.has(ch)) return 0; + return null; +} + /** Offset used for the characters that need no special placement. */ const NO_OFFSET = Object.freeze({ dx: 0, dy: 0 }); @@ -426,9 +439,10 @@ function drawText(ctx, layout, state, font) { const x = left + (lines.length - 1 - i) * step; for (let j = 0; j < chars.length; j++) { const ch = chars[j]; - if (VERTICAL_ROTATED.has(ch)) { + const drop = verticalRotation(ch); + if (drop !== null) { ctx.save(); - ctx.translate(x + fontSize / 2, top + j * fontSize + fontSize / 2 + VERTICAL_ROTATED_DROP * fontSize); + ctx.translate(x + fontSize / 2, top + j * fontSize + fontSize / 2 + drop * fontSize); ctx.rotate(Math.PI / 2); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; @@ -492,9 +506,10 @@ function liveText(state, layout, font, fill, places) { const x = left + (lines.length - 1 - i) * step; for (let j = 0; j < chars.length; j++) { const ch = chars[j]; - if (VERTICAL_ROTATED.has(ch)) { + const drop = verticalRotation(ch); + if (drop !== null) { const cx = x + fontSize / 2; - const cy = top + j * fontSize + fontSize / 2 + VERTICAL_ROTATED_DROP * fontSize; + const cy = top + j * fontSize + fontSize / 2 + drop * fontSize; rotated.push(`${escapeText(ch)}`); -- cgit v1.3.1