diff --git a/arona-core/build.gradle.kts b/arona-core/build.gradle.kts index 76ffe93..9c7b215 100644 --- a/arona-core/build.gradle.kts +++ b/arona-core/build.gradle.kts @@ -43,9 +43,10 @@ dependencies { // https://mvnrepository.com/artifact/com.github.taptap/pinyin-plus implementation("com.github.taptap:pinyin-plus:1.0") implementation("net.mamoe.yamlkt:yamlkt-jvm:0.10.2") - val skikoVersion = "0.7.68" + val skikoVersion = "0.7.72" implementation("org.jetbrains.skiko:skiko-awt-runtime-windows-x64:$skikoVersion") implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$skikoVersion") + implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-arm64:$skikoVersion") // implementation("org.jetbrains.skiko:skiko-awt-runtime-x64:$skikoVersion") } diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/advance/ActivityNotify.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/advance/ActivityNotify.kt index 5738753..78e03ee 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/advance/ActivityNotify.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/advance/ActivityNotify.kt @@ -25,6 +25,7 @@ object ActivityNotify: AronaQuartzService { private const val ActivityNotifyDataInitKey = "init" private const val ActivityNotifyOneHour = "ActivityNotifyOneHour" private const val ActivityKey = "activity" + private const val NotifyStringKey = "notifyString" private const val MaintenanceKey = "maintenance" private const val DropActivityTime = 22 private const val DropEndTime = 24 + 3 - DropActivityTime @@ -32,26 +33,33 @@ object ActivityNotify: AronaQuartzService { class ActivityNotifyJob: Job { override fun execute(context: JobExecutionContext?) { - val jp: Pair, List> = ActivityUtil.fetchJPActivity() + val jp = ActivityUtil.fetchJPActivity() val en = ActivityUtil.fetchENActivity() + val cn = ActivityUtil.fetchCNActivity() val alertListJP = mutableListOf() val alertListEN = mutableListOf() + val alertListCN = mutableListOf() val filterJP = jp.first .filter { filterActive(it, alertListJP) } to jp.second .filter { filterPending(it) - } + }.also { insertAlert(alertListJP, ServerLocale.JP) } val filterEN = en.first .filter { filterActive(it, alertListEN) } to en.second .filter { filterPending(it) - } - insertAlert(alertListJP) - insertAlert(alertListEN) + }.also { insertAlert(alertListEN, ServerLocale.GLOBAL) } + val filterCN = cn.first + .filter { + filterActive(it, alertListCN) + } to cn.second + .filter { + filterPending(it) + }.also { insertAlert(alertListCN, ServerLocale.CN) } // 初始化不显示信息 val init = context?.mergedJobDataMap?.getBoolean(ActivityNotifyDataInitKey) ?: false if (init) return @@ -64,6 +72,10 @@ object ActivityNotify: AronaQuartzService { val enMessage = ActivityUtil.createActivityImage(filterEN, ServerLocale.GLOBAL) sendMessage(enMessage, AronaNotifyConfig.notifyStringEN) } + if (AronaNotifyConfig.enableCN) { + val enMessage = ActivityUtil.createActivityImage(filterCN, ServerLocale.CN) + sendMessage(enMessage, AronaNotifyConfig.notifyStringCN) + } } } @@ -78,21 +90,24 @@ object ActivityNotify: AronaQuartzService { } } - private fun doInsert(activity: List, h: Int, extraKey: String = "") { + private fun doInsert(activity: List, h: Int, locale: ServerLocale, extraKey: String = "") { val now = Calendar.getInstance() now.set(Calendar.HOUR_OF_DAY, h) now.set(Calendar.MINUTE, 0) now.set(Calendar.MILLISECOND, 0) - val serverJp = isJPServer(activity) QuartzProvider.createSingleTask( ActivityNotifyOneHourJob::class.java, now.time, - "${ActivityNotifyOneHour}-${if (serverJp) "jp" else "en"}-${h}-${extraKey}", + "${ActivityNotifyOneHour}-${locale.commandName}-${h}-${extraKey}", ActivityNotifyOneHour, - mapOf(ActivityKey to activity) + mapOf(ActivityKey to activity, NotifyStringKey to when(locale) { + ServerLocale.JP -> AronaNotifyConfig.notifyStringJP + ServerLocale.GLOBAL -> AronaNotifyConfig.notifyStringEN + ServerLocale.CN -> AronaNotifyConfig.notifyStringCN + }) ) } - private fun insertAlert(activity: MutableList) { + private fun insertAlert(activity: MutableList, locale: ServerLocale) { if (activity.isEmpty()) return val instance = Calendar.getInstance() val dropActivities = activity.filter { isMidnightEndActivity(it) } @@ -101,26 +116,19 @@ object ActivityNotify: AronaQuartzService { val nowH = instance.get(Calendar.HOUR_OF_DAY) if (activity.isNotEmpty()) { activity.groupBy { calcDiffDayAndHour(it.time).first }.forEach { (h, u) -> - doInsert(u, nowH + h - 1, h.toString()) + doInsert(u, nowH + h - 1, locale, h.toString()) } } // 双倍掉落提醒 if (dropActivities.isNotEmpty()) { - doInsert(dropActivities, DropActivityTime) + doInsert(dropActivities, DropActivityTime, locale) } } - private fun insertMaintenanceAlert(activity: Activity, h: Int) { - doInsert(listOf(activity), Calendar.getInstance().get(Calendar.HOUR_OF_DAY) + h - 1, extraKey = MaintenanceKey) - } - private fun filterPending(activity: Activity): Boolean { val extra = calcDiffDayAndHour(activity.time) val h = extra.first val d = extra.second - if ((d == 0) && isMaintenanceActivity(activity)) { - insertMaintenanceAlert(activity, h) - } return doFilter(d, h) } @@ -147,7 +155,6 @@ object ActivityNotify: AronaQuartzService { val ac = context?.mergedJobDataMap?.get(ActivityKey) ?: return ac as List if (ac.isEmpty()) return - val server = isJPServer(ac) val activity = ac.toMutableList() val maintenance: Activity? = activity .filter { isMaintenanceActivity(it) } @@ -163,12 +170,15 @@ object ActivityNotify: AronaQuartzService { if (maintenance != null) { Arona.sendMessage("距离${serverName}维护还有1小时") } + val notifyPrefix = context.mergedJobDataMap?.get(NotifyStringKey) ?: "${serverName}防侠提醒" // 只有维护信息时 if (activity.isEmpty()) return val activityString = activity .map { at -> "${at.content}\n" } .reduceOrNull { prv, cur -> prv + cur } - val serverString = MiraiCode.deserializeMiraiCode(if (server) AronaNotifyConfig.notifyStringJP else AronaNotifyConfig.notifyStringEN) + val serverString = if (context.mergedJobDataMap?.get(NotifyStringKey) != null) { + MiraiCode.deserializeMiraiCode(notifyPrefix as String) + } else notifyPrefix val endTime = if (isMidnightEndActivity(activity[0])) { DropEndTime } else { @@ -190,10 +200,6 @@ object ActivityNotify: AronaQuartzService { private fun isMaintenanceActivity(activity: Activity): Boolean = activity.type == ActivityType.MAINTENANCE - private fun isJPServer(activity: List) = isJPServer(activity[0]) - - private fun isJPServer(activity: Activity) = activity.serverLocale == ServerLocale.JP - override fun init() { registerService() } @@ -219,4 +225,4 @@ object ActivityNotify: AronaQuartzService { // 每日防侠提醒类型 enum class NotifyType { ALL, ONLY_24H, ONLY_48H -} \ No newline at end of file +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/command/ActivityCommand.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/command/ActivityCommand.kt index 5c429cc..e7aa4a1 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/command/ActivityCommand.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/command/ActivityCommand.kt @@ -22,6 +22,7 @@ object ActivityCommand : SimpleCommand( when(locale) { ServerLocale.JP -> sendJP(subject) ServerLocale.GLOBAL -> sendEN(subject) + ServerLocale.CN -> sendCN(subject) } } when { @@ -49,7 +50,8 @@ object ActivityCommand : SimpleCommand( } private suspend fun sendCN(subject: Contact) { - subject.sendMessage("国服还没开呢,别急") + val cnActivity = ActivityUtil.fetchCNActivity() + send(subject, cnActivity, ServerLocale.CN) } private suspend fun send(subject: Contact, activities: Pair, List>, serverLocale: ServerLocale = ServerLocale.JP) { @@ -65,4 +67,4 @@ object ActivityCommand : SimpleCommand( registerService() register() } -} \ No newline at end of file +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/config/AronaNotifyConfig.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/config/AronaNotifyConfig.kt index 3d568f7..e6f4e9f 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/config/AronaNotifyConfig.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/config/AronaNotifyConfig.kt @@ -30,6 +30,12 @@ object AronaNotifyConfig: AutoSavePluginConfig("arona-notify") { @ValueDescription("国际服防侠提醒开头文字") val notifyStringEN: String by value("arona的防侠预警(国际服)") - @ValueDescription("\"/活动\"指令的默认目标服务器,可选值为 \"JP\"和\"GLOBAL\"") + @ValueDescription("启用国服防侠提醒") + val enableCN: Boolean by value(true) + + @ValueDescription("国服防侠提醒开头文字") + val notifyStringCN: String by value("arona的防侠预警(国服)") + + @ValueDescription("\"/活动\"指令的默认目标服务器,可选值为 \"JP\"、\"GLOBAL\"和\"CN\"") val defaultActivityCommandServer: ServerLocale by value(ServerLocale.JP) } diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/entity/Activity.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/entity/Activity.kt index 30edeee..76a2264 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/entity/Activity.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/entity/Activity.kt @@ -10,7 +10,7 @@ data class Activity( enum class ServerLocale(val serverName: String, val dbName: String, val commandName: String) { JP("日服", "JPN", "jp"), GLOBAL("国际服", "GLB", "en"), -// CN("国服", "CN", "cn"), + CN("国服", "CN", "cn"), } enum class ActivityType(val level: Int) { @@ -28,4 +28,4 @@ enum class ActivityType(val level: Int) { MAINTENANCE(4), // 维护 ACTIVITY(2), // 普通活动 BIRTHDAY(5) //生日 -} \ No newline at end of file +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/entity/schaleDB/CommonDAO.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/entity/schaleDB/CommonDAO.kt index 83408d5..53635ec 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/entity/schaleDB/CommonDAO.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/entity/schaleDB/CommonDAO.kt @@ -166,6 +166,7 @@ data class CommonDAO( val terrain = when(type){ ServerLocale.GLOBAL -> item.getOrNull(Raid.CurrentGLB)!! ServerLocale.JP -> item.getOrNull(Raid.CurrentJPN)!! + ServerLocale.CN -> item.getOrNull(Raid.CurrentGLB)!! } dao.regions[serverType].current_raid = dao.regions[serverType].current_raid.plus(CurrentRaid(value, terrain, start, end)) } @@ -231,4 +232,4 @@ data class CurrentRaid( data class ChangeLog( val date : String, val contents : List -) \ No newline at end of file +) diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/ActivityUtil.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/ActivityUtil.kt index 7a5e548..e1ea2e0 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/util/ActivityUtil.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/ActivityUtil.kt @@ -39,7 +39,7 @@ object ActivityUtil { private const val WikiruPage = "イベント一覧" const val DEFAULT_CALENDAR_FONT_SIZE = 144 const val DEFAULT_CALENDAR_LINE_MARGIN = 30 - const val DEFAULT_IMAGE_SCALE = 0.2F + const val DEFAULT_IMAGE_SCALE = 0.15F const val ServerMaintenanceStartTimeEN = "11:00" const val ServerMaintenanceStartTimeJP = "10:00" const val ServerMaintenanceEndTimeEN = "15:00" @@ -73,8 +73,9 @@ object ActivityUtil { private fun fetchENActivityFromSchaleDB(): Pair, List> = SchaleDBUtil.getGlobalEventData() - private fun fetchENActivityFromGameKee(): Pair, List> = - GameKeeUtil.getEventData(ServerLocale.GLOBAL) + private fun fetchENActivityFromGameKee() = GameKeeUtil.getEventData(ServerLocale.GLOBAL) + + private fun fetchCNActivityFromGameKee() = GameKeeUtil.getEventData(ServerLocale.CN) fun fetchENActivity(): Pair, List> { return fetchENActivityFromGameKee().let { @@ -83,6 +84,13 @@ object ActivityUtil { } } + fun fetchCNActivity(): Pair, List> { + return fetchCNActivityFromGameKee().let { + // 加入生日信息 + it.first to it.second.toMutableList().also { a -> a.addAll(SchaleDBUtil.getENBirthdayData()) } + } + } + fun fetchJPActivity(): Pair, List> { return fetchJPActivityFromGameKee().let { it.first to it.second.toMutableList().also { a -> a.addAll(SchaleDBUtil.getJPBirthdayData()) } } } @@ -533,7 +541,7 @@ object ActivityUtil { } private fun extraActivityJPTypeFromCN(activity: Activity): Activity { - return activity + return extraActivityTypeFromGameKee(activity, ServerLocale.JP) } private fun extraActivityTypeFromGameKee(activity: Activity, server: ServerLocale): Activity { @@ -630,6 +638,39 @@ object ActivityUtil { doInsert0(now, parseStart, parseEnd, active, pending, activity) } + /** + * 插入国服活动并对活动进行分类 + * @param now 当前时间 + * @param parseStart 活动开始时间 + * @param parseEnd 活动结束时间 + * @param active 正在进行的活动列表 + * @param pending 即将开始的活动列表 + * @param contentSource 活动内容 + * @param type0 已知的活动类型(如果已经得知可以直接填,未知请在本方法里注册方法解析出来) + */ + fun insertCnActivity( + now: Date, + parseStart: Date, + parseEnd: Date, + active: MutableList, + pending: MutableList, + contentSource: String, + from: ActivityCNSource = ActivityCNSource.GAME_KEE, + type0: ActivityType = ActivityType.NULL + ) { + var activity = Activity( + contentSource, + TimeUtil.calcTime(parseStart, true), + serverLocale = ServerLocale.CN, + ) + if (type0 == ActivityType.NULL) { + activity = extraActivityTypeFromGameKee(activity, ServerLocale.CN) + } else { + activity.type = type0 + } + doInsert0(now, parseStart, parseEnd, active, pending, activity) + } + /** * 插入活动并对活动进行分类 @@ -655,8 +696,6 @@ object ActivityUtil { } } - private fun locale(type: ActivityType?): ServerLocale = if (type == null) ServerLocale.JP else ServerLocale.GLOBAL - private fun sortActive(active: Activity): Int { val diff = calcDiffDayAndHour(active.time) return diff.first * 24 + diff.second @@ -716,8 +755,8 @@ object ActivityUtil { drawActivity(pending, true) ImageUtil.drawText(image, "数据来源: https://ba.gamekee.com/", calcY()) val imageFile = File(Arona.dataFolder.absolutePath + "/activity-${server.serverName}.png") - image.first.scale(DEFAULT_IMAGE_SCALE, DEFAULT_IMAGE_SCALE).makeImageSnapshot().also { - ImageIO.write(ImageIO.read(ByteArrayInputStream(it.encodeToData()?.bytes)), "png", imageFile) + image.first.scale(DEFAULT_IMAGE_SCALE, DEFAULT_IMAGE_SCALE).also { + ImageIO.write(it, "png", imageFile) } return imageFile } @@ -726,12 +765,17 @@ object ActivityUtil { B_WIKI("https://wiki.biligame.com/bluearchive/"), WIKI_RU("https://bluearchive.wikiru.jp/"), GAME_KEE("https://ba.gamekee.com/"), - SCHALE_DB("https://lonqie.github.io/SchaleDB/") + SCHALE_DB("https://schale.gg/") } enum class ActivityENSource(val source: String) { - SCHALE_DB("https://lonqie.github.io/SchaleDB/"), + SCHALE_DB("https://schale.gg/"), BILIBILI("https://space.bilibili.com/1585224247"), GAME_KEE("https://ba.gamekee.com/") } + + enum class ActivityCNSource(val source: String) { + GAME_KEE("https://ba.gamekee.com/"), + SCHALE_DB("https://schale.gg/"), + } } diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/FontUtils.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/FontUtils.kt new file mode 100644 index 0000000..ba0cb5d --- /dev/null +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/FontUtils.kt @@ -0,0 +1,168 @@ +package net.diyigemt.arona.util + +import org.jetbrains.skia.* +import org.jetbrains.skia.paragraph.* +import java.util.* +import kotlin.collections.* +import kotlin.jvm.* + +/** + * @author cssxsh + * @see [https://github.com/cssxsh/mirai-skia-plugin/blob/main/src/main/kotlin/xyz/cssxsh/skia/FontUtils.kt] + */ +/** + * 获取字体工具 + * @see Typeface + * @see FontMgr + * @see TypefaceFontProvider + * @see [https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list] + * @see [https://github.com/cssxsh/mirai-skia-plugin/blob/main/src/main/kotlin/xyz/cssxsh/skia/FontUtils.kt] + */ +public object FontUtils { + + @PublishedApi + internal val instances: Sequence = sequence { + yield(provider) + yield(FontMgr.default) + yieldAll(ServiceLoader.load(FontMgr::class.java)) + yieldAll(ServiceLoader.load(TypefaceFontProvider::class.java)) + } + + public val provider: TypefaceFontProvider = TypefaceFontProvider() + + /** + * 字体列表 + */ + public fun families(): Set { + val names: MutableSet = HashSet() + for (manager in instances) { + repeat(manager.familiesCount) { index -> names.add(manager.getFamilyName(index)) } + } + + return names + } + + /** + * 加载字体 + * @see provider + */ + public fun loadTypeface(path: String, index: Int = 0) { + provider.registerTypeface(Typeface.makeFromFile(path, index)) + } + + /** + * 加载字体 + * @see provider + */ + public fun loadTypeface(data: Data, index: Int = 0) { + provider.registerTypeface(Typeface.makeFromData(data, index)) + } + + /** + * 加载字体 + * @see provider + */ + public fun loadTypeface(bytes: ByteArray, index: Int = 0) { + Data.makeFromBytes(bytes).use { data -> loadTypeface(data, index) } + } + + /** + * 获取指定的 [Typeface] + */ + public fun matchFamilyStyle(familyName: String, style: FontStyle): Typeface? { + for (provider in instances) { + return provider.matchFamily(familyName).matchStyle(style) ?: continue + } + return null + } + + /** + * 获取指定的 [Typeface] + */ + public fun matchFamiliesStyle(families: Array, style: FontStyle): Typeface? { + for (provider in instances) { + for (familyName in families) { + return provider.matchFamily(familyName).matchStyle(style) ?: continue + } + } + return null + } + + /** + * 获取指定的 [FontStyleSet] (count != 0) + */ + public fun matchFamily(familyName: String): FontStyleSet? { + for (provider in instances) { + val styles = provider.matchFamily(familyName) + if (styles.count() != 0) { + return styles + } + } + return null + } + + /** + * 宋体 + */ + public fun matchSimSun(style: FontStyle): Typeface? = matchFamilyStyle("SimSun", style) + + /** + * 新宋体 + */ + public fun matchNSimSun(style: FontStyle): Typeface? = matchFamilyStyle("NSimSun", style) + + /** + * 黑体 + */ + public fun matchSimHei(style: FontStyle): Typeface? = matchFamilyStyle("SimHei", style) + + /** + * 仿宋 + */ + public fun matchFangSong(style: FontStyle): Typeface? = matchFamilyStyle("FangSong", style) + + /** + * 楷体 + */ + public fun matchKaiTi(style: FontStyle): Typeface? = matchFamilyStyle("KaiTi", style) + + /** + * Arial + */ + public fun matchArial(style: FontStyle): Typeface? = matchFamilyStyle("Arial", style) + + /** + * Calibri + */ + public fun matchCalibri(style: FontStyle): Typeface? = matchFamilyStyle("Calibri", style) + + /** + * Consolas + */ + public fun matchConsolas(style: FontStyle): Typeface? = matchFamilyStyle("Consolas", style) + + /** + * Times New Roman + */ + public fun matchTimesNewRoman(style: FontStyle): Typeface? = matchFamilyStyle("Times New Roman", style) + + /** + * Helvetica + */ + public fun matchHelvetica(style: FontStyle): Typeface? = matchFamilyStyle("Helvetica", style) + + /** + * Liberation Sans + */ + public fun matchLiberationSans(style: FontStyle): Typeface? = matchFamilyStyle("Liberation Sans", style) + + /** + * Liberation Serif + */ + public fun matchLiberationSerif(style: FontStyle): Typeface? = matchFamilyStyle("Liberation Serif", style) + + /** + * Noto Color Emoji + */ + public fun matchNotoColorEmoji(style: FontStyle): Typeface? = matchFamilyStyle("Noto Color Emoji", style) +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/GameKeeUtil.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/GameKeeUtil.kt index eff1724..d8c1dd9 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/util/GameKeeUtil.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/GameKeeUtil.kt @@ -32,10 +32,12 @@ object GameKeeUtil { val method = when(server){ ServerLocale.GLOBAL -> ActivityUtil::insertEnActivity ServerLocale.JP -> ActivityUtil::insertJpActivity + ServerLocale.CN -> ActivityUtil::insertCnActivity } val source = when(server){ ServerLocale.GLOBAL -> ActivityUtil.ActivityENSource.GAME_KEE ServerLocale.JP -> ActivityUtil.ActivityJPSource.GAME_KEE + ServerLocale.CN -> ActivityUtil.ActivityCNSource.GAME_KEE } for(i in json.data){ @@ -55,4 +57,4 @@ object GameKeeUtil { return active to pending } -} \ No newline at end of file +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/ImageUtil.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/ImageUtil.kt index 895ddd8..6e3ed46 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/util/ImageUtil.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/ImageUtil.kt @@ -7,6 +7,10 @@ import net.diyigemt.arona.util.ActivityUtil.DEFAULT_CALENDAR_LINE_MARGIN import okhttp3.OkHttpClient import okhttp3.Request import org.jetbrains.skia.* +import java.awt.Color +import java.awt.Image +import java.awt.image.BufferedImage +import java.io.ByteArrayInputStream import java.io.File import javax.imageio.ImageIO import kotlin.math.max @@ -61,16 +65,21 @@ object ImageUtil : InitializedFunction() { drawTextAlign(group, str, 0, y, align, color) } - fun Surface.scale(x: Float, y: Float, color: Int = 0xFFFFFFFF.toInt()): Surface { + fun Surface.scale(x: Float, y: Float, color: Int = 0xFFFFFFFF.toInt()): BufferedImage { val width = (this.width * x).toInt() val height = (this.height * y).toInt() - val after = Surface.makeRasterN32Premul(width, height) - after.canvas.clear(color) - after.canvas.scale(x, y).drawImage(this.makeImageSnapshot(), 0f, 0f) + val bf = ImageIO.read(ByteArrayInputStream(this.makeImageSnapshot().encodeToData()?.bytes)) + val scale = bf.getScaledInstance(width, height, Image.SCALE_SMOOTH) + val after = BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR) + after.graphics.drawImage(scale, 0, 0, Color.WHITE, null) + return after } - private fun Paint(color: Int) = Paint().also { it.color = color } + private fun Paint(color: Int) = Paint().also { + it.color = color + it.isAntiAlias = true + } enum class TextAlign { LEFT, RIGHT, CENTER @@ -80,23 +89,42 @@ object ImageUtil : InitializedFunction() { // 下载字体 Arona.runSuspend { kotlin.runCatching { - File(Arona.dataFolderPath(FontFolder)).also { it.mkdirs() } + File(Arona.dataFolderPath(FontFolder)).apply { mkdirs() } val path = "$FontFolder/$FONT_NAME" val fontFile = Arona.dataFolderFile(path) if (!fontFile.exists()) { NetworkUtil.downloadFileFile(path, fontFile) } val tf = Typeface.makeFromFile(fontFile.path) - font = Font(tf).also { - it.edging = FontEdging.SUBPIXEL_ANTI_ALIAS - it.hinting = FontHinting.FULL + font = Font(tf, size = DEFAULT_CALENDAR_FONT_SIZE.toFloat()).apply { + edging = FontEdging.SUBPIXEL_ANTI_ALIAS + hinting = FontHinting.FULL } Arona.info("中文字体初始化成功") }.onFailure { - font = Font(Typeface.makeDefault()) + font = Font( + FontUtils.matchFamiliesStyle(arrayOf("Arial", "Noto Serif SC", "黑体"), FontStyle.BOLD), + size = DEFAULT_CALENDAR_FONT_SIZE.toFloat() + ).apply { + edging = FontEdging.SUBPIXEL_ANTI_ALIAS + hinting = FontHinting.FULL + } Arona.warning("字体注册失败, 使用默认字体, 可能会导致中文乱码") } } } } +fun FontMgr.makeFamilies(): Map { + val count = familiesCount + if (count == 0) return emptyMap() + val families: MutableMap = HashMap() + + for (index in 0 until count) { + val name = getFamilyName(index) + val styles = makeStyleSet(index) ?: throw NoSuchElementException("${this}: ${index}.${name}") + families[name] = styles + } + + return families +} diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBDataSyncService.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBDataSyncService.kt index 75ceb31..6f67d8d 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBDataSyncService.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBDataSyncService.kt @@ -64,10 +64,10 @@ object SchaleDBDataSyncService : AronaQuartzService{ fun getBirthdayList() { val formatter = DateTimeFormatter.ofPattern("yyyy/M/d") SchaleDBUtil.birthdayList.clear() - for (item in SchaleDBUtil.studentItem){ - val date = LocalDate.parse(LocalDate.now().year.toString() + "/" + item.BirthDay, formatter) + SchaleDBUtil.studentItem.forEach { + val date = LocalDate.parse(LocalDate.now().year.toString() + "/" + it.BirthDay, formatter) if (date.isAfter(LocalDate.now()) && date.isBefore(LocalDate.now().plusWeeks(1))){ - SchaleDBUtil.birthdayList.add(Birthday(item.Name, date)) + SchaleDBUtil.birthdayList.add(Birthday(it.Name, date)) } } } diff --git a/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBUtil.kt b/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBUtil.kt index 0a584d1..48cd7c6 100644 --- a/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBUtil.kt +++ b/arona-core/src/main/kotlin/net/diyigemt/arona/util/scbaleDB/SchaleDBUtil.kt @@ -35,10 +35,12 @@ object SchaleDBUtil { val function = when(type) { ServerLocale.JP -> ActivityUtil::insertJpActivity ServerLocale.GLOBAL -> ActivityUtil::insertEnActivity + ServerLocale.CN -> ActivityUtil::insertCnActivity } val source = when(type) { ServerLocale.JP -> ActivityUtil.ActivityJPSource.SCHALE_DB ServerLocale.GLOBAL -> ActivityUtil.ActivityENSource.SCHALE_DB + ServerLocale.CN -> ActivityUtil.ActivityCNSource.SCHALE_DB } //Characters for (item in commonItem.regions[type.ordinal].current_gacha) { @@ -122,25 +124,27 @@ object SchaleDBUtil { val function = when(locale) { ServerLocale.JP -> ActivityUtil::insertJpActivity ServerLocale.GLOBAL -> ActivityUtil::insertEnActivity + ServerLocale.CN -> ActivityUtil::insertCnActivity } val source = when(locale) { ServerLocale.JP -> ActivityUtil.ActivityJPSource.SCHALE_DB ServerLocale.GLOBAL -> ActivityUtil.ActivityENSource.SCHALE_DB + ServerLocale.CN -> ActivityUtil.ActivityCNSource.SCHALE_DB } if (birthdayList.isEmpty()) SchaleDBDataSyncService.BirthdayJob().getBirthdayList() val now = Calendar.getInstance().time - for (item in birthdayList){ + birthdayList.filter { !it.name.contains("(") }.forEach { function.call( now, - Date.from(item.date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()), - Date.from(item.date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()), + Date.from(it.date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()), + Date.from(it.date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()), mutableListOf(), list, - item.name + "的生日", + it.name + "的生日", source, ActivityType.BIRTHDAY ) } return list } -} \ No newline at end of file +}