diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index e5bbb6b..c17db16 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -102,6 +102,9 @@ kotlin { } } +compose.resources { + publicResClass = true +} android { namespace = "mg.dot.feufaro" compileSdk = libs.versions.android.compileSdk.get().toInt() diff --git a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt index 62df64a..15306fb 100644 --- a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt +++ b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt @@ -253,16 +253,22 @@ private fun drawNoteWithSubscript( text: String, x: Float, y: Float ) { var curX = x - var i = 0 - while (i < text.length) { - val str = text[i].toString() + val superscriptOffset = fontSize * 0.35f + + 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.setFont(font, fontSize) - cs.newLineAtOffset(curX, y) - cs.showText(str.sanitize()) + cs.setFont(font, currentSize) + cs.newLineAtOffset(curX, currentY) + cs.showText(str) cs.endText() - curX += font.getStringWidth(str.sanitize()) / 1000f * fontSize - i++ + curX += font.getStringWidth(str) / 1000f * currentSize + 0.5f } } @@ -446,11 +452,11 @@ private suspend fun generatePdfToBytes( val customNbGrid = settings.nbGridOnMeasure // ── Polices ──────────────────────────────────────────────────────────────── - val noteFontBytes = Res.readBytes("files/PTSerif-Regular.ttf") - val lyricFontBytes = Res.readBytes("files/LinLibertine_R.ttf") - val markerItalicBoldFontBytes = Res.readBytes("files/PT Serif Bold Italic.ttf") - val markerBoldFontBytes = Res.readBytes("files/PTSerif-Bold.ttf") - val emmentalerFontBytes = Res.readBytes("files/emmentaler-20.ttf") + val noteFontBytes = Res.readBytes("font/PTSerif-Regular.ttf") + val lyricFontBytes = Res.readBytes("font/LinLibertine_R.ttf") + val markerItalicBoldFontBytes = Res.readBytes("font/PT_Serif_Bold_Italic.ttf") + val markerBoldFontBytes = Res.readBytes("font/PTSerif_Bold.ttf") + val emmentalerFontBytes = Res.readBytes("font/Emmentaler.ttf") PDDocument().use { doc -> @@ -611,6 +617,15 @@ private suspend fun generatePdfToBytes( 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 if (markerText.isNotBlank() && !markerText.contains("𝄐")) { val dsRegex = Regex("""D\.?S\.?""") diff --git a/composeApp/src/commonMain/composeResources/font/Emmentaler.ttf b/composeApp/src/commonMain/composeResources/font/Emmentaler.ttf new file mode 100644 index 0000000..778067a Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/Emmentaler.ttf differ diff --git a/composeApp/src/commonMain/composeResources/files/LinLibertine_R.ttf b/composeApp/src/commonMain/composeResources/font/LinLibertine_R.ttf similarity index 100% rename from composeApp/src/commonMain/composeResources/files/LinLibertine_R.ttf rename to composeApp/src/commonMain/composeResources/font/LinLibertine_R.ttf diff --git a/composeApp/src/commonMain/composeResources/files/PTSerif-Regular.ttf b/composeApp/src/commonMain/composeResources/font/PTSerif-Regular.ttf similarity index 100% rename from composeApp/src/commonMain/composeResources/files/PTSerif-Regular.ttf rename to composeApp/src/commonMain/composeResources/font/PTSerif-Regular.ttf diff --git a/composeApp/src/commonMain/composeResources/font/PTSerif_Bold.ttf b/composeApp/src/commonMain/composeResources/font/PTSerif_Bold.ttf new file mode 100644 index 0000000..36d47eb Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/PTSerif_Bold.ttf differ diff --git a/composeApp/src/commonMain/composeResources/font/PT_Serif_Bold_Italic.ttf b/composeApp/src/commonMain/composeResources/font/PT_Serif_Bold_Italic.ttf new file mode 100644 index 0000000..da5a256 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/font/PT_Serif_Bold_Italic.ttf differ diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/HarmonicAnalyser.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/HarmonicAnalyser.kt index 3aa3a25..a23ae6b 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/HarmonicAnalyser.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/HarmonicAnalyser.kt @@ -33,28 +33,28 @@ object HarmonicAnalyzer { ) private val chordDefinitions = listOf( - ChordDef("maj7", setOf(0, 4, 7, 11), setOf(4, 11), 12), - ChordDef("7", setOf(0, 4, 7, 10), setOf(4, 10), 12), - ChordDef("m7", setOf(0, 3, 7, 10), setOf(3, 10), 12), - ChordDef("dim7", setOf(0, 3, 6, 9), setOf(3, 6, 9), 12), - ChordDef("m7b5", setOf(0, 3, 6, 10), setOf(3, 6, 10), 12), - ChordDef("7sus4", setOf(0, 5, 7, 10), setOf(5, 10), 12), - ChordDef("maj7sus2", setOf(0, 2, 7, 11), setOf(2, 11), 12), - ChordDef("maj7#11", setOf(0, 4, 6, 7), setOf(4, 6), 12), - ChordDef("add9", setOf(0, 2, 4, 7), setOf(2, 4), 11), - ChordDef("madd9", setOf(0, 2, 3, 7), setOf(2, 3), 11), - ChordDef("add11", setOf(0, 4, 5, 7), setOf(4, 5), 11), + ChordDef("maj⁷", setOf(0, 4, 7, 11), setOf(4, 11), 12), + ChordDef("⁷", setOf(0, 4, 7, 10), setOf(4, 10), 12), + ChordDef("ₘ⁷", setOf(0, 3, 7, 10), setOf(3, 10), 12), + ChordDef("dim⁷", setOf(0, 3, 6, 9), setOf(3, 6, 9), 12), + ChordDef("ₘ⁷b⁵", setOf(0, 3, 6, 10), setOf(3, 6, 10), 12), + ChordDef("⁷sus⁴", setOf(0, 5, 7, 10), setOf(5, 10), 12), + ChordDef("maj⁷sus²", setOf(0, 2, 7, 11), setOf(2, 11), 12), + ChordDef("maj⁷#¹¹", setOf(0, 4, 6, 7), setOf(4, 6), 12), + ChordDef("add⁹", setOf(0, 2, 4, 7), setOf(2, 4), 11), + ChordDef("ₘadd⁹", setOf(0, 2, 3, 7), setOf(2, 3), 11), + ChordDef("add¹¹", setOf(0, 4, 5, 7), setOf(4, 5), 11), 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("aug", setOf(0, 4, 8), setOf(4, 8), 8), - ChordDef("sus2", setOf(0, 2, 7), setOf(2), 8), - ChordDef("sus4", setOf(0, 5, 7), setOf(5), 8), + ChordDef("sus²", setOf(0, 2, 7), setOf(2), 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("m", setOf(0, 3), setOf(3), 5), + ChordDef("ₘ", setOf(0, 3), setOf(3), 5), ChordDef("", setOf(0), setOf(), 3) ) diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt index 375b10f..82eadbc 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -43,6 +43,10 @@ import androidx.compose.ui.text.style.BaselineShift import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* 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.launch import kotlinx.coroutines.withContext @@ -54,6 +58,7 @@ import mg.dot.feufaro.getPathValidator import mg.dot.feufaro.ui.rememberFileSaveLauncher import mg.dot.feufaro.viewmodel.MidiMarkers import mg.dot.feufaro.viewmodel.SolfaScreenModel +import org.jetbrains.compose.resources.Font import java.io.File import kotlin.math.min import kotlin.math.roundToInt @@ -689,6 +694,11 @@ fun LazyVerticalGridTUO( val totalSyllables = sharedScreenModel.synchronizedSyllables.value.size //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 containerWidthDp = gridWidthDp / gridColumnCount val REGEX_CLEAN_PREFIX = Regex("(\\d+\\.)+") @@ -757,6 +767,7 @@ fun LazyVerticalGridTUO( val lineHeightDp : Dp = with(density) { lineHeight.toDp() } + var markerFontSize: Float = MaterialTheme.typography.titleMedium.fontSize.value var fontStyle = FontStyle.Normal var fontWeight = FontWeight.Normal val hairPinSymbol = tuo.hasHairPin() @@ -811,11 +822,21 @@ fun LazyVerticalGridTUO( // problème si template = $QD:,-$QD tuo.pTemplate.resetCalledMarker() val text = tuo.pTemplate.markerToString() - if (text.contains(Regex("^([mfp]+|[rR]it.*)$"))) { - fontStyle = FontStyle.Italic - } - if (text.contains(Regex("^\\s*(ppp|pp|p|mp|mf|f|ff|fff)\\s*$"))) { - fontWeight = FontWeight.Bold + 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) + val musicForEmmentRegex = Regex("""\b(ppp|pp|mp|mf|fff|ff|p|f|sfz|sf|fz|rfz|fp|pf)\b""", RegexOption.IGNORE_CASE) + + when { + 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, modifier = Modifier @@ -823,10 +844,11 @@ fun LazyVerticalGridTUO( softWrap = false, maxLines = 1, fontStyle = fontStyle, - fontWeight = fontWeight, - style = TextStyle( + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.titleMedium.copy( color = MaterialTheme.colorScheme.onSecondary, - fontSize = MaterialTheme.typography.titleMedium.fontSize + fontSize = markerFontSize.sp, + fontFamily = markerFontFamily ) ) } diff --git a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt index 3cdf935..d2b1bfd 100644 --- a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt +++ b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/pdf/PdfExport.kt @@ -414,6 +414,10 @@ private fun drawNoteWithModulation( parts.forEachIndexed { index, part -> 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 // 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 ? @@ -427,10 +431,10 @@ private fun drawNoteWithModulation( cs.beginText() cs.setTextMatrix(Matrix.getTranslateInstance(currentX, currentY)) cs.setFont(font, currentSize) - cs.showText(part) + cs.showText(sanitizedPart) 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 ) { var curX = x - var i = 0 - while (i < text.length) { - val ch = text[i] - val str = ch.toString() + val superscriptOffset = fontSize * 0.35f + + 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() - when(ch) { - '₁', '₂', '₃', '¹', '²', '³' -> { - cs.setFont(font, fontSize * 0.6f) - } else -> { - cs.setFont(font, fontSize) - } - } - cs.newLineAtOffset(curX, y) - cs.showText(str.sanitize()) + cs.setFont(font, currentSize) + cs.newLineAtOffset(curX, currentY) + cs.showText(str) cs.endText() - curX += font.getStringWidth(str.sanitize()) / 1000f * fontSize - i++ + curX += font.getStringWidth(str) / 1000f * currentSize + 0.5f } } @@ -647,11 +650,11 @@ private suspend fun generatePdfToBytes( val customNbGrid = settings.nbGridOnMeasure // ── Polices ──────────────────────────────────────────────────── - val noteFontBytes = Res.readBytes("files/PTSerif-Regular.ttf") - val lyricFontBytes = Res.readBytes("files/LinLibertine_R.ttf") - val markerItalicBoldFontBytes = Res.readBytes("files/PT Serif Bold Italic.ttf") - val markerBoldFontBytes = Res.readBytes("files/PTSerif-Bold.ttf") - val emmentalerFontBytes = Res.readBytes("files/emmentaler-20.ttf") + val noteFontBytes = Res.readBytes("font/PTSerif-Regular.ttf") + val lyricFontBytes = Res.readBytes("font/LinLibertine_R.ttf") + val markerItalicBoldFontBytes = Res.readBytes("font/PT_Serif_Bold_Italic.ttf") + val markerBoldFontBytes = Res.readBytes("font/PTSerif_Bold.ttf") + val emmentalerFontBytes = Res.readBytes("font/Emmentaler.ttf") 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 - if (markerText.isNotBlank() && !markerText.contains("𝄐")) { + if (markerText.isNotBlank() && !markerText.contains("𝄐") && !markerText.contains("♩")) { val dsRegex = Regex("""D\.?S\.?""") val dcRegex = Regex("""D\.?C\.?""") 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) } else if (markerText.contains(dsRegex) ||