Use musical fonts for specific markers
This commit is contained in:
parent
9550ab0e3c
commit
90c7b40d84
10 changed files with 114 additions and 61 deletions
|
|
@ -102,6 +102,9 @@ kotlin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compose.resources {
|
||||||
|
publicResClass = true
|
||||||
|
}
|
||||||
android {
|
android {
|
||||||
namespace = "mg.dot.feufaro"
|
namespace = "mg.dot.feufaro"
|
||||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
|
|
||||||
|
|
@ -253,16 +253,22 @@ private fun drawNoteWithSubscript(
|
||||||
text: String, x: Float, y: Float
|
text: String, x: Float, y: Float
|
||||||
) {
|
) {
|
||||||
var curX = x
|
var curX = x
|
||||||
var i = 0
|
val superscriptOffset = fontSize * 0.35f
|
||||||
while (i < text.length) {
|
|
||||||
val str = text[i].toString()
|
for (char in text) {
|
||||||
|
val str = char.toString().sanitize()
|
||||||
|
val isSuperscript = char in setOf('¹', '²', '³')
|
||||||
|
val isSubscript = char in setOf('₁', '₂', '₃')
|
||||||
|
|
||||||
|
val currentSize = if (isSuperscript || isSubscript) fontSize * 0.6f else fontSize
|
||||||
|
val currentY = if (isSuperscript) y + superscriptOffset else y
|
||||||
|
|
||||||
cs.beginText()
|
cs.beginText()
|
||||||
cs.setFont(font, fontSize)
|
cs.setFont(font, currentSize)
|
||||||
cs.newLineAtOffset(curX, y)
|
cs.newLineAtOffset(curX, currentY)
|
||||||
cs.showText(str.sanitize())
|
cs.showText(str)
|
||||||
cs.endText()
|
cs.endText()
|
||||||
curX += font.getStringWidth(str.sanitize()) / 1000f * fontSize
|
curX += font.getStringWidth(str) / 1000f * currentSize + 0.5f
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -446,11 +452,11 @@ private suspend fun generatePdfToBytes(
|
||||||
val customNbGrid = settings.nbGridOnMeasure
|
val customNbGrid = settings.nbGridOnMeasure
|
||||||
|
|
||||||
// ── Polices ────────────────────────────────────────────────────────────────
|
// ── Polices ────────────────────────────────────────────────────────────────
|
||||||
val noteFontBytes = Res.readBytes("files/PTSerif-Regular.ttf")
|
val noteFontBytes = Res.readBytes("font/PTSerif-Regular.ttf")
|
||||||
val lyricFontBytes = Res.readBytes("files/LinLibertine_R.ttf")
|
val lyricFontBytes = Res.readBytes("font/LinLibertine_R.ttf")
|
||||||
val markerItalicBoldFontBytes = Res.readBytes("files/PT Serif Bold Italic.ttf")
|
val markerItalicBoldFontBytes = Res.readBytes("font/PT_Serif_Bold_Italic.ttf")
|
||||||
val markerBoldFontBytes = Res.readBytes("files/PTSerif-Bold.ttf")
|
val markerBoldFontBytes = Res.readBytes("font/PTSerif_Bold.ttf")
|
||||||
val emmentalerFontBytes = Res.readBytes("files/emmentaler-20.ttf")
|
val emmentalerFontBytes = Res.readBytes("font/Emmentaler.ttf")
|
||||||
|
|
||||||
PDDocument().use { doc ->
|
PDDocument().use { doc ->
|
||||||
|
|
||||||
|
|
@ -611,6 +617,15 @@ private suspend fun generatePdfToBytes(
|
||||||
drawFermata(cs, colX + colWidth / 2 - 5f, markerY - 2f, 0.3f)
|
drawFermata(cs, colX + colWidth / 2 - 5f, markerY - 2f, 0.3f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (markerText.contains("♩")) {
|
||||||
|
val textPart = markerText.replace("♩", "BPM")
|
||||||
|
cs.beginText()
|
||||||
|
cs.setFont(italicBoldFont, fontSize)
|
||||||
|
cs.newLineAtOffset(colX, markerY)
|
||||||
|
cs.setFont(boldFont, fontSize-3f)
|
||||||
|
cs.showText(textPart)
|
||||||
|
cs.endText()
|
||||||
|
}
|
||||||
// Marqueur texte
|
// Marqueur texte
|
||||||
if (markerText.isNotBlank() && !markerText.contains("𝄐")) {
|
if (markerText.isNotBlank() && !markerText.contains("𝄐")) {
|
||||||
val dsRegex = Regex("""D\.?S\.?""")
|
val dsRegex = Regex("""D\.?S\.?""")
|
||||||
|
|
|
||||||
BIN
composeApp/src/commonMain/composeResources/font/Emmentaler.ttf
Normal file
BIN
composeApp/src/commonMain/composeResources/font/Emmentaler.ttf
Normal file
Binary file not shown.
BIN
composeApp/src/commonMain/composeResources/font/PTSerif_Bold.ttf
Normal file
BIN
composeApp/src/commonMain/composeResources/font/PTSerif_Bold.ttf
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -33,28 +33,28 @@ object HarmonicAnalyzer {
|
||||||
)
|
)
|
||||||
|
|
||||||
private val chordDefinitions = listOf(
|
private val chordDefinitions = listOf(
|
||||||
ChordDef("maj7", setOf(0, 4, 7, 11), setOf(4, 11), 12),
|
ChordDef("maj⁷", setOf(0, 4, 7, 11), setOf(4, 11), 12),
|
||||||
ChordDef("7", setOf(0, 4, 7, 10), setOf(4, 10), 12),
|
ChordDef("⁷", setOf(0, 4, 7, 10), setOf(4, 10), 12),
|
||||||
ChordDef("m7", setOf(0, 3, 7, 10), setOf(3, 10), 12),
|
ChordDef("ₘ⁷", setOf(0, 3, 7, 10), setOf(3, 10), 12),
|
||||||
ChordDef("dim7", setOf(0, 3, 6, 9), setOf(3, 6, 9), 12),
|
ChordDef("dim⁷", setOf(0, 3, 6, 9), setOf(3, 6, 9), 12),
|
||||||
ChordDef("m7b5", setOf(0, 3, 6, 10), setOf(3, 6, 10), 12),
|
ChordDef("ₘ⁷b⁵", setOf(0, 3, 6, 10), setOf(3, 6, 10), 12),
|
||||||
ChordDef("7sus4", setOf(0, 5, 7, 10), setOf(5, 10), 12),
|
ChordDef("⁷sus⁴", setOf(0, 5, 7, 10), setOf(5, 10), 12),
|
||||||
ChordDef("maj7sus2", setOf(0, 2, 7, 11), setOf(2, 11), 12),
|
ChordDef("maj⁷sus²", setOf(0, 2, 7, 11), setOf(2, 11), 12),
|
||||||
ChordDef("maj7#11", setOf(0, 4, 6, 7), setOf(4, 6), 12),
|
ChordDef("maj⁷#¹¹", setOf(0, 4, 6, 7), setOf(4, 6), 12),
|
||||||
ChordDef("add9", setOf(0, 2, 4, 7), setOf(2, 4), 11),
|
ChordDef("add⁹", setOf(0, 2, 4, 7), setOf(2, 4), 11),
|
||||||
ChordDef("madd9", setOf(0, 2, 3, 7), setOf(2, 3), 11),
|
ChordDef("ₘadd⁹", setOf(0, 2, 3, 7), setOf(2, 3), 11),
|
||||||
ChordDef("add11", setOf(0, 4, 5, 7), setOf(4, 5), 11),
|
ChordDef("add¹¹", setOf(0, 4, 5, 7), setOf(4, 5), 11),
|
||||||
|
|
||||||
ChordDef("", setOf(0, 4, 7), setOf(4), 8),
|
ChordDef("", setOf(0, 4, 7), setOf(4), 8),
|
||||||
ChordDef("m", setOf(0, 3, 7), setOf(3), 8),
|
ChordDef("ₘ", setOf(0, 3, 7), setOf(3), 8),
|
||||||
ChordDef("dim", setOf(0, 3, 6), setOf(3, 6), 8),
|
ChordDef("dim", setOf(0, 3, 6), setOf(3, 6), 8),
|
||||||
ChordDef("aug", setOf(0, 4, 8), setOf(4, 8), 8),
|
ChordDef("aug", setOf(0, 4, 8), setOf(4, 8), 8),
|
||||||
ChordDef("sus2", setOf(0, 2, 7), setOf(2), 8),
|
ChordDef("sus²", setOf(0, 2, 7), setOf(2), 8),
|
||||||
ChordDef("sus4", setOf(0, 5, 7), setOf(5), 8),
|
ChordDef("sus⁴", setOf(0, 5, 7), setOf(5), 8),
|
||||||
|
|
||||||
ChordDef("5", setOf(0, 7), setOf(7), 5),
|
ChordDef("⁵", setOf(0, 7), setOf(7), 5),
|
||||||
ChordDef("", setOf(0, 4), setOf(4), 5),
|
ChordDef("", setOf(0, 4), setOf(4), 5),
|
||||||
ChordDef("m", setOf(0, 3), setOf(3), 5),
|
ChordDef("ₘ", setOf(0, 3), setOf(3), 5),
|
||||||
|
|
||||||
ChordDef("", setOf(0), setOf(), 3)
|
ChordDef("", setOf(0), setOf(), 3)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ import androidx.compose.ui.text.style.BaselineShift
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
|
import feufaro.composeapp.generated.resources.Emmentaler
|
||||||
|
import feufaro.composeapp.generated.resources.PTSerif_Bold
|
||||||
|
import feufaro.composeapp.generated.resources.PT_Serif_Bold_Italic
|
||||||
|
import feufaro.composeapp.generated.resources.Res
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -54,6 +58,7 @@ import mg.dot.feufaro.getPathValidator
|
||||||
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
||||||
import mg.dot.feufaro.viewmodel.MidiMarkers
|
import mg.dot.feufaro.viewmodel.MidiMarkers
|
||||||
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
|
import org.jetbrains.compose.resources.Font
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
@ -689,6 +694,11 @@ fun LazyVerticalGridTUO(
|
||||||
val totalSyllables = sharedScreenModel.synchronizedSyllables.value.size
|
val totalSyllables = sharedScreenModel.synchronizedSyllables.value.size
|
||||||
//println("Sync complète effectuée SUR stz: ${sharedScreenModel.stanza.value}! Total colonnes : $totalSyllables")
|
//println("Sync complète effectuée SUR stz: ${sharedScreenModel.stanza.value}! Total colonnes : $totalSyllables")
|
||||||
}
|
}
|
||||||
|
var emmentaler = FontFamily(Font(Res.font.Emmentaler))
|
||||||
|
var ptSerifBoldItalic = FontFamily(Font(Res.font.PT_Serif_Bold_Italic))
|
||||||
|
var ptSerif = FontFamily(Font(Res.font.PTSerif_Bold))
|
||||||
|
var markerFontFamily: FontFamily = FontFamily.Default
|
||||||
|
|
||||||
val textMeasurer = rememberTextMeasurer()
|
val textMeasurer = rememberTextMeasurer()
|
||||||
val containerWidthDp = gridWidthDp / gridColumnCount
|
val containerWidthDp = gridWidthDp / gridColumnCount
|
||||||
val REGEX_CLEAN_PREFIX = Regex("(\\d+\\.)+")
|
val REGEX_CLEAN_PREFIX = Regex("(\\d+\\.)+")
|
||||||
|
|
@ -757,6 +767,7 @@ fun LazyVerticalGridTUO(
|
||||||
val lineHeightDp : Dp = with(density) {
|
val lineHeightDp : Dp = with(density) {
|
||||||
lineHeight.toDp()
|
lineHeight.toDp()
|
||||||
}
|
}
|
||||||
|
var markerFontSize: Float = MaterialTheme.typography.titleMedium.fontSize.value
|
||||||
var fontStyle = FontStyle.Normal
|
var fontStyle = FontStyle.Normal
|
||||||
var fontWeight = FontWeight.Normal
|
var fontWeight = FontWeight.Normal
|
||||||
val hairPinSymbol = tuo.hasHairPin()
|
val hairPinSymbol = tuo.hasHairPin()
|
||||||
|
|
@ -811,11 +822,21 @@ fun LazyVerticalGridTUO(
|
||||||
// problème si template = $QD:,-$QD
|
// problème si template = $QD:,-$QD
|
||||||
tuo.pTemplate.resetCalledMarker()
|
tuo.pTemplate.resetCalledMarker()
|
||||||
val text = tuo.pTemplate.markerToString()
|
val text = tuo.pTemplate.markerToString()
|
||||||
if (text.contains(Regex("^([mfp]+|[rR]it.*)$"))) {
|
val musicForPtSerifBIRegex = Regex("""^(Largh\.|Grave|Largo|Lento|Adagio|And\.|Andantino|Mod\.|Moderato|Alleg\.|All\.|Viv\.|Vivacissimo|Presto|Prestiss\.|accel\.|rit\.|rall\.|riten\.|string\.|allarg\.|a tempo|Tempo I|rubato|meno mosso|più mosso|cres\.|<|decresc\.|dim\.|>|rfz|fp|pf|sub\.p|sub\.f)""", RegexOption.IGNORE_CASE)
|
||||||
fontStyle = FontStyle.Italic
|
val musicForEmmentRegex = Regex("""\b(ppp|pp|mp|mf|fff|ff|p|f|sfz|sf|fz|rfz|fp|pf)\b""", RegexOption.IGNORE_CASE)
|
||||||
}
|
|
||||||
if (text.contains(Regex("^\\s*(ppp|pp|p|mp|mf|f|ff|fff)\\s*$"))) {
|
when {
|
||||||
fontWeight = FontWeight.Bold
|
musicForPtSerifBIRegex.containsMatchIn(text) -> {
|
||||||
|
markerFontSize = MaterialTheme.typography.titleMedium.fontSize.value
|
||||||
|
markerFontFamily = ptSerifBoldItalic
|
||||||
|
}
|
||||||
|
musicForEmmentRegex.containsMatchIn(text) -> {
|
||||||
|
markerFontSize = MaterialTheme.typography.titleMedium.fontSize.value+11
|
||||||
|
markerFontFamily = emmentaler
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
markerFontFamily = ptSerif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Text(text = text,
|
Text(text = text,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -823,10 +844,11 @@ fun LazyVerticalGridTUO(
|
||||||
softWrap = false,
|
softWrap = false,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
fontStyle = fontStyle,
|
fontStyle = fontStyle,
|
||||||
fontWeight = fontWeight,
|
fontWeight = FontWeight.Bold,
|
||||||
style = TextStyle(
|
style = MaterialTheme.typography.titleMedium.copy(
|
||||||
color = MaterialTheme.colorScheme.onSecondary,
|
color = MaterialTheme.colorScheme.onSecondary,
|
||||||
fontSize = MaterialTheme.typography.titleMedium.fontSize
|
fontSize = markerFontSize.sp,
|
||||||
|
fontFamily = markerFontFamily
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,10 @@ private fun drawNoteWithModulation(
|
||||||
parts.forEachIndexed { index, part ->
|
parts.forEachIndexed { index, part ->
|
||||||
if (part.isEmpty()) return@forEachIndexed
|
if (part.isEmpty()) return@forEachIndexed
|
||||||
|
|
||||||
|
val sanitizedPart = part
|
||||||
|
.replace('\u2015', '-')
|
||||||
|
.replace('\u2014', '-')
|
||||||
|
.replace('\u2013', '-')
|
||||||
// RÈGLE : Si c'est avant le dernier segment, c'est un exposant
|
// RÈGLE : Si c'est avant le dernier segment, c'est un exposant
|
||||||
// Dans ">t>m", 't' est à l'index 1 (exposant), 'm' est à l'index 2 (normal)
|
// Dans ">t>m", 't' est à l'index 1 (exposant), 'm' est à l'index 2 (normal)
|
||||||
// ATTENTION : selon votre logique, si c'est ">t>m", t=exposant, m=normal ?
|
// ATTENTION : selon votre logique, si c'est ">t>m", t=exposant, m=normal ?
|
||||||
|
|
@ -427,10 +431,10 @@ private fun drawNoteWithModulation(
|
||||||
cs.beginText()
|
cs.beginText()
|
||||||
cs.setTextMatrix(Matrix.getTranslateInstance(currentX, currentY))
|
cs.setTextMatrix(Matrix.getTranslateInstance(currentX, currentY))
|
||||||
cs.setFont(font, currentSize)
|
cs.setFont(font, currentSize)
|
||||||
cs.showText(part)
|
cs.showText(sanitizedPart)
|
||||||
cs.endText()
|
cs.endText()
|
||||||
|
|
||||||
currentX += textWidth(font, currentSize, part) + 0.5f
|
currentX += textWidth(font, currentSize, sanitizedPart) + 0.5f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,23 +447,22 @@ private fun drawNoteWithSubscript(
|
||||||
y: Float
|
y: Float
|
||||||
) {
|
) {
|
||||||
var curX = x
|
var curX = x
|
||||||
var i = 0
|
val superscriptOffset = fontSize * 0.35f
|
||||||
while (i < text.length) {
|
|
||||||
val ch = text[i]
|
for (char in text) {
|
||||||
val str = ch.toString()
|
val str = char.toString().sanitize()
|
||||||
|
val isSuperscript = char in setOf('¹', '²', '³')
|
||||||
|
val isSubscript = char in setOf('₁', '₂', '₃')
|
||||||
|
|
||||||
|
val currentSize = if (isSuperscript || isSubscript) fontSize * 0.6f else fontSize
|
||||||
|
val currentY = if (isSuperscript) y + superscriptOffset else y
|
||||||
|
|
||||||
cs.beginText()
|
cs.beginText()
|
||||||
when(ch) {
|
cs.setFont(font, currentSize)
|
||||||
'₁', '₂', '₃', '¹', '²', '³' -> {
|
cs.newLineAtOffset(curX, currentY)
|
||||||
cs.setFont(font, fontSize * 0.6f)
|
cs.showText(str)
|
||||||
} else -> {
|
|
||||||
cs.setFont(font, fontSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cs.newLineAtOffset(curX, y)
|
|
||||||
cs.showText(str.sanitize())
|
|
||||||
cs.endText()
|
cs.endText()
|
||||||
curX += font.getStringWidth(str.sanitize()) / 1000f * fontSize
|
curX += font.getStringWidth(str) / 1000f * currentSize + 0.5f
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -647,11 +650,11 @@ private suspend fun generatePdfToBytes(
|
||||||
val customNbGrid = settings.nbGridOnMeasure
|
val customNbGrid = settings.nbGridOnMeasure
|
||||||
|
|
||||||
// ── Polices ────────────────────────────────────────────────────
|
// ── Polices ────────────────────────────────────────────────────
|
||||||
val noteFontBytes = Res.readBytes("files/PTSerif-Regular.ttf")
|
val noteFontBytes = Res.readBytes("font/PTSerif-Regular.ttf")
|
||||||
val lyricFontBytes = Res.readBytes("files/LinLibertine_R.ttf")
|
val lyricFontBytes = Res.readBytes("font/LinLibertine_R.ttf")
|
||||||
val markerItalicBoldFontBytes = Res.readBytes("files/PT Serif Bold Italic.ttf")
|
val markerItalicBoldFontBytes = Res.readBytes("font/PT_Serif_Bold_Italic.ttf")
|
||||||
val markerBoldFontBytes = Res.readBytes("files/PTSerif-Bold.ttf")
|
val markerBoldFontBytes = Res.readBytes("font/PTSerif_Bold.ttf")
|
||||||
val emmentalerFontBytes = Res.readBytes("files/emmentaler-20.ttf")
|
val emmentalerFontBytes = Res.readBytes("font/Emmentaler.ttf")
|
||||||
|
|
||||||
PDDocument().use { doc ->
|
PDDocument().use { doc ->
|
||||||
|
|
||||||
|
|
@ -831,13 +834,23 @@ private suspend fun generatePdfToBytes(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (markerText.contains("♩")) {
|
||||||
|
val textPart = markerText.replace("♩", "BPM")
|
||||||
|
cs.beginText()
|
||||||
|
cs.setFont(italicBoldFont, fontSize)
|
||||||
|
cs.newLineAtOffset(colX, markerY)
|
||||||
|
cs.setFont(boldFont, fontSize-3f)
|
||||||
|
cs.showText(textPart)
|
||||||
|
cs.endText()
|
||||||
|
}
|
||||||
|
|
||||||
// marqueur
|
// marqueur
|
||||||
if (markerText.isNotBlank() && !markerText.contains("𝄐")) {
|
if (markerText.isNotBlank() && !markerText.contains("𝄐") && !markerText.contains("♩")) {
|
||||||
val dsRegex = Regex("""D\.?S\.?""")
|
val dsRegex = Regex("""D\.?S\.?""")
|
||||||
val dcRegex = Regex("""D\.?C\.?""")
|
val dcRegex = Regex("""D\.?C\.?""")
|
||||||
|
|
||||||
cs.beginText()
|
cs.beginText()
|
||||||
if(markerText.matches(Regex("^\\s*(ppp|pp|p|mp|mf|f|ff|fff)\\s*$"))) {
|
if(markerText.matches(Regex("^\\s*(ppp|pp|mp|mf|fff|ff|p|f|sfz|sf|fz|rfz|fp|pf)\\s*$"))) {
|
||||||
cs.setFont(emmentalerFont, fontSize+10f)
|
cs.setFont(emmentalerFont, fontSize+10f)
|
||||||
}
|
}
|
||||||
else if (markerText.contains(dsRegex) ||
|
else if (markerText.contains(dsRegex) ||
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue