Initial commit

This commit is contained in:
2025-02-10 03:05:25 +08:00
commit 57a0ecc385
93 changed files with 6685 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
# About This Document
> This document is powered by [VuePress](https://v2.vuepress.vuejs.org/en).
## License
[Apache-2.0](https://github.com/HighCapable/YukiReflection/blob/master/LICENSE)
```:no-line-numbers
Apache License Version 2.0
Copyright (C) 2019 HighCapable
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
https://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.
```
Copyright © 2019 HighCapable

View File

@@ -0,0 +1,27 @@
# Changelog
> The version update history of `PanguText` is recorded here.
::: danger
We will only maintain the latest API version, if you are using an outdate API version, you voluntarily renounce any possibility of maintenance.
:::
::: warning
To avoid translation time consumption, Changelog will use **Google Translation** from **Chinese** to **English**, please refer to the original text for actual reference.
Time zone of version release date: **UTC+8**
:::
## pangutext-android
### 1.0.0 | 2025.02.10 &ensp;<Badge type="tip" text="latest" vertical="middle" />
- The first version is submitted to Maven
## pangutext-compose
Not yet released.

View File

@@ -0,0 +1,16 @@
# Contact Us
> If you have any questions in use, or have any constructive suggestions, you can contact us.
Join our developers group.
- [Click to join Telegram group](https://t.me/BetterAndroid)
- [Click to join Telegram group (Developer)](https://t.me/HighCapable_Dev)
Find me on **Twitter** [@fankesyooni](https://twitter.com/fankesyooni).
## Help with Maintenance
Thank you for choosing and using `PanguText`.
If you have code-related suggestions and requests, you can submit a Pull Request on GitHub.

View File

@@ -0,0 +1,15 @@
# Looking for Future
> The future is bright and uncertain, let us look forward to the future development space of `PanguText`.
## Future Plans
> Features that `PanguText` may add later are included here.
### Limitations of SpannableString
`PanguText`'s main functionality on the Android platform currently comes from `SpannableString`, which has not yet fully resolved the issues of handling complex text styles and performance overhead.
### Jetpack Compose Plan
`PanguText` will support Jetpack Compose in the future and plans to use `AnnotatedString` as the main text processing method to minimize intrusion into the underlying layer.

View File

@@ -0,0 +1,6 @@
# R8 & Proguard Obfuscate
> In most scenarios, the app packages can be compressed through obfuscation,
> here is an introduction to how to configure obfuscation rules.
`PanguText` does not require any additional obfuscation rules.

View File

@@ -0,0 +1,70 @@
# Introduce
> `PanguText` is a solution for CJK (Chinese, Japanese, Korean) and English word, half-width number spacing.
## Background
This project was created because, until now, there hasnt been a public solution to perfectly address the typography issues between Chinese, Japanese, Korean, and English.
Typically, when mixing CJK (i.e. Chinese, Japanese, Korean) with English, aesthetic issues can arise—a historical legacy stemming from the differences in writing conventions between full-width and half-width characters. Although the W3C has now established CJK typography guidelines, only a few individuals or companies willing to adhere to these standards have adopted this approach.
Currently, the known vendor solutions are as follows:
- Apple platforms (iOS, iPadOS, macOS, tvOS, watchOS) text typography solutions
- Xiaomis (HyperOS) text typography optimization
- OrginOSs font-based text typography optimization
However, these solutions are closed and cannot be implemented on other platforms.
We aim to provide an open-source solution adaptable to various scenarios, featuring low intrusiveness and easy integration, allowing more developers to effectively address text typography issues.
The primary inspiration for this project comes from [pangu.js](https://github.com/vinta/pangu.js), which offers a set of regular expressions for CJK typography.
We have optimized these solutions to format text across platforms without inserting extra space characters. We extend this approach further to explore additional possibilities.
Heartfelt thanks to the original developer of **pangu.js** for providing the foundational solution.
## Effects
As you can see, the typography scheme of `PanguText` does not work by simply inserting spaces between CJK characters and English words.
Instead, it leverages each platform's native handling to automatically add whitespace between these characters, ensuring minimal intrusion.
> Before Applying (Top) vs. After Applying (Bottom)
<img src="/images/demo_01.png" width="300" />
> Dynamic Application
<img src="/images/demo_02.gif" width="480" />
`PanguText` supports dynamic application, which means it can add whitespace gaps to each character on-the-fly as you input text.
::: tip Developer's Perspective
I personally do not recommend manually inserting spaces between CJK and English characters for typographic refinement if your software or system natively supports enhanced typographic formatting.
The spacing can vary across fonts, which may lead to formatting issues and the insertion of undesired space characters.
In certain contexts, such as URLs, filenames, or hashtags containing “#”, these spaces are not acceptable.
However, in special scenarios—for example, within code comments or documentation—it can be beneficial to add spaces, as these areas typically do not employ automated formatting tools.
Another point to consider is the use of different punctuation marks in different languages.
Avoid mixing full-width and half-width punctuation marks.
If you must use half-width punctuation marks to annotate full-width text, ensure that the half-width marks are followed by a space to complete the character space (the same applies to English).
:::
## Language Requirement
It is recommended to use Kotlin as the preferred development language.
This project is entirely written in Kotlin and is compatible with Java in some parts, but it may not be fully compatible.
All demo & sample codes in the document will be described using Kotlin, if you dont know how to use Kotlin at all, you may not get the best experience.
## Contribution
The maintenance of this project is inseparable from the support and contributions of all developers.
This project is currently in its early stages, and there may still be some problems or lack of functions you need.
If possible, feel free to submit a PR to contribute features you think are needed to this project or goto [GitHub Issues](repo://issues)
to make suggestions to us.

View File

@@ -0,0 +1,86 @@
# Quick Start
> Integrate `PanguText` into your project.
## Project Requirements
The project needs to be created using `Android Studio` or `IntelliJ IDEA` and be of type Android or Kotlin Multiplatform
project and have integrated Kotlin environment dependencies.
- Android Studio (It is recommended to get the latest version [from here](https://developer.android.com/studio))
- IntelliJ IDEA (It is recommended to get the latest version [from here](https://www.jetbrains.com/idea))
- Kotlin 1.9.0+, Gradle 8+, Java 17+, Android Gradle Plugin 8+
### Configure Repositories
The dependencies of `PanguText` are published in **Maven Central** and our public repository,
you can use the following method to configure repositories.
We recommend using Kotlin DSL as the Gradle build script language and [SweetDependency](https://github.com/HighCapable/SweetDependency)
to manage dependencies.
#### SweetDependency (Recommended)
Configure repositories in your project's `SweetDependency` configuration file.
```yaml
repositories:
google:
maven-central:
# (Optional) You can add this URL to use our public repository
# When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
# For details, please visit: https://github.com/HighCapable/maven-repository
highcapable-maven-releases:
url: https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases
```
#### Traditional Method
Configure repositories in your project `build.gradle.kts`.
```kotlin
repositories {
google()
mavenCentral()
// (Optional) You can add this URL to use our public repository
// When Sonatype-OSS fails and cannot publish dependencies, this repository is added as a backup
// For details, please visit: https://github.com/HighCapable/maven-repository
maven("https://raw.githubusercontent.com/HighCapable/maven-repository/main/repository/releases")
}
```
### Configure Java Version
Modify the Java version of Kotlin in your project `build.gradle.kts` to 17 or above.
> Kotlin DSL
```kt
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
```
## Functional Overview
The project is divided into multiple modules: Android platform and Jetpack Compose (multiplatform). You can choose the module you wish to include as a dependency in your project.
Click the corresponding module below to view detailed feature descriptions.
- [Android](../library/android.md)
- [Jetpack Compose](../library/compose.md)
## Demo
You can find some examples below. Check out the corresponding demo projects to get a better understanding of how these features work and quickly select the functionality you need.
- [Android](repo://tree/main/demo-android)
- [Jetpack Compose (Coming soon)](repo://tree/main/demo-compose)

View File

@@ -0,0 +1,13 @@
---
home: true
title: Home
heroImage: /images/logo.png
actions:
- text: Get Started
link: /en/guide/home
type: primary
- text: Changelog
link: /en/about/changelog
type: secondary
footer: Apache-2.0 License | Copyright (C) 2019 HighCapable
---

View File

@@ -0,0 +1,367 @@
# Android
![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.pangutext/pangutext-android?logo=apachemaven&logoColor=orange)
<span style="margin-left: 5px"/>
![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fpangutext%2Fpangutext-android%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases)
<span style="margin-left: 5px"/>
![Android Min SDK](https://img.shields.io/badge/Min%20SDK-21-orange?logo=android)
This is the core dependency for the Android platform. When using `PanguText` on Android, you need to include this module.
## Configure Dependency
You can add this module to your project using the following method.
### SweetDependency (Recommended)
Add dependency in your project's `SweetDependency` configuration file.
```yaml
libraries:
com.highcapable.pangutext:
pangutext-android:
version: +
```
Configure dependency in your project `build.gradle.kts`.
```kotlin
implementation(com.highcapable.pangutext.pangutext.android)
```
### Traditional Method
Configure dependency in your project `build.gradle.kts`.
```kotlin
implementation("com.highcapable.pangutext:pangutext-android:<version>")
```
Please change `<version>` to the version displayed at the top of this document.
## Function Introduction
You can view the KDoc [click here](kdoc://pangutext-android).
### Implementation Principle
`PanguText` provides two methods for text formatting on the Android platform: `SpannableString` (does not alter the original text length) and direct insertion of whitespace characters (alters the original text length).
The first method, `SpannableString`, adds a `Span` with spacing to the character before the one that needs spacing, changing the text style without altering the string content. The rendering is done by the `TextView` layer (or manually using `TextPaint` based on `Spanned` for layout styling), achieving non-intrusive text styling.
This method also supports processing already styled text (`Spanned`), such as text created via `Html.fromHtml`.
**However, it is currently experimental and may still have unexpected style errors**. You can refer to the [Personalized Configuration](#personalized-configuration) section below to disable it.
The dynamic application (injection) feature mainly targets the input state of `EditText`. It sets a custom `TextWatcher` for `EditText` to monitor input changes and formats the text from `afterTextChanged`.
The second method directly inserts whitespace characters after the characters that need spacing. This method alters the original text length and content but does not rely on the `TextView` layer for rendering. It uses `TextPaint` to draw the text directly, suitable for all scenarios, **but does not support dynamic application (injection)**.
::: warning Unresolved Issues
`PanguText` may conflict with Material components like `TextInputEditText`, `MaterialAutoCompleteTextView`, and `TextInputLayout` when using `setHint`, as `TextView` does not account for `Span` during measurement. This issue is particularly noticeable in single-line text, and there is no solution yet. Use these components cautiously.
Due to the above issue, calculating the width of a `TextView` with `PanguText` style using the `View.measure` method may also result in errors.
`PanguText` currently cannot handle continuous characters like underlines or strikethroughs in `Spanned` text, as the lines will break after adding spacing. It may also cause style errors or fail to apply styles correctly to some special characters. For stability, avoid enabling `PanguText` for very complex rich text or refer to the [Personalized Configuration](#personalized-configuration) section to set `excludePatterns`.
:::
### Integrate into Existing Projects
Integrating `PanguText` into your current project is very easy. You don't need to change much code. Choose your preferred method below to complete the integration.
#### Inject to LayoutInflater
`PanguText` supports direct injection of `LayoutInflater.Factory2` or creating a `LayoutInflater.Factory2` instance for the current `Activity` to take over the entire view. This is the recommended integration method, as it allows for non-intrusive and quick integration without modifying any existing layouts.
> The following example
```kotlin
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Inject here.
PanguTextFactory2.inject(this)
setContentView(binding.root)
}
}
```
::: tip
Since `LayoutInflater.Factory2` is taken over, recycled layouts like `ListView` and `RecyclerView` can also be correctly taken over.
After injecting the `LayoutInflater` instance in the `Activity`, the following instances attached to the current `Context` will automatically take effect:
- `Fragment`
- `Dialog`
- `PopupWindow`
- `Toast` (foreground only in higher system versions)
Layouts based on `RemoteView` will not take effect because they are remote objects and do not use the current `Context`'s `LayoutInflater` for layout loading.
:::
If you are using [ui-component → AppBindingActivity](https://betterandroid.github.io/BetterAndroid/KDoc/ui-component/ui-component/com.highcapable.betterandroid.ui.component.activity/-app-binding-activity) in `BetterAndroid`, you need to slightly modify the current code.
> The following example
```kotlin
class MainActivity : AppBindingActivity<ActivityMainBinding>() {
override fun onPrepareContentView(savedInstanceState: Bundle?): LayoutInflater {
val inflater = super.onPrepareContentView(savedInstanceState)
// Inject here.
PanguTextFactory2.inject(inflater)
return inflater
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Your code here.
}
}
```
If your application does not use `AppCompatActivity` or `ViewBinding`, don't worry, you can still use the original method.
> The following example
```kotlin
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Inject here.
PanguTextFactory2.inject(this)
setContentView(R.layout.activity_main)
}
}
```
::: tip
`PanguTextFactory2` can be used not only with `Activity` but also injected into any existing `LayoutInflater` instance.
However, please inject before the `LayoutInflater` instance is used to load the layout, otherwise it will not take effect.
:::
#### Manual Injection or Text Formatting
`PanguText` also supports manual injection, allowing you to inject it into the desired `TextView` or `EditText`.
> The following example
```kotlin
// Assume this is your TextView.
val textView: TextView
// Assume this is your EditText.
val editText: EditText
// Inject into existing text.
textView.injectPanguText()
editText.injectPanguText()
// Optionally choose whether to inject Hint (default is true).
textView.injectPanguText(injectHint = false)
editText.injectPanguText(injectHint = false)
// Dynamic injection, re-calling setText will automatically take effect.
textView.injectRealTimePanguText()
// Dynamic injection mainly targets the input state of EditText.
editText.injectRealTimePanguText()
// Optionally choose whether to inject Hint (default is true).
textView.injectRealTimePanguText(injectHint = false)
editText.injectRealTimePanguText(injectHint = false)
```
`PanguText` also extends the `setText` method of `TextView`, allowing you to directly set text with `PanguText` style.
> The following example
```kotlin
// Assume this is your TextView.
val textView: TextView
// Set text with PanguText style.
textView.setTextWithPangu("Xiaoming今年16岁")
// Set Hint with PanguText style.
textView.setHintWithPangu("输入Xiaoming的年龄")
```
You can also use the `PanguText.format` method to directly format text.
> The following example
```kotlin
// Assume this is your TextView.
val textView: TextView
// Format text using SpannableString method.
// Requires passing the current TextView's Resources and text size.
// If the input text is already Spannable,
// it will return the original object without creating a new SpannableString.
val text = PanguText.format(textView.resources, textView.textSize, "Xiaoming今年16岁")
// Set text.
textView.text = text
// Directly format text using whitespace characters for insertion.
// This method adds extra whitespace characters "" (HSP) to the text.
// The result below will output the string "Xiaoming今年16岁".
// You can also customize the whitespace character at the end of the method.
val text = PanguText.format("Xiaoming今年16岁")
// Set text.
textView.text = text
```
::: tip
The `injectPanguText`, `injectRealTimePanguText`, `setTextWithPangu`, `setHintWithPangu`, and `PanguText.format` methods support the `config` parameter.
You can refer to the [Personalized Configuration](#personalized-configuration) section below.
:::
#### Custom View
`PanguText` can also be used with custom `View`. You can extend your `View` to `AppCompatTextView` and override the `setText` method.
> The following example
```kotlin
class MyTextView(context: Context, attrs: AttributeSet? = null) : AppCompatTextView(context, attrs) {
override fun setText(text: CharSequence?, type: BufferType?) {
// Manually inject here.
val panguText = text?.let { PanguText.format(resources, textSize, it) }
super.setText(panguText, type)
}
}
```
::: warning
After injecting `PanguText` into `TextView`, if you use `android:singleLine="true"` in XML layout or `TextView.setSingleLine(true)` in code along with `android:ellipsize="..."`,
this method of setting single-line text may cause unresolvable `OBJ` characters (truncated by ellipsis) to appear when the text exceeds the screen width, because `TextView` does not account for `Span` during measurement, leading to incorrect text width calculation.
The solution is to use `android:maxLines="1"` in XML layout or `TextView.setMaxLines(1)` in code instead.
> The following example
```xml
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一段很长很长长长长长长长长长长长长还有English混入的的文本"
android:maxLines="1"
android:ellipsize="end" />
```
:::
### Personalized Configuration
`PanguText` supports personalized configuration. You can use the global static instance `PanguText.globalConfig` to get the global configuration or configure it individually.
> The following example
```kotlin
// Get global configuration.
val config = PanguText.globalConfig
// Enable or disable the feature.
config.isEnabled = true
// Process Spanned text.
// Processing Spanned text is enabled by default, but this feature is experimental.
// If issues occur, you can disable it. When disabled, Spanned text will return the original text.
config.isProcessedSpanned = true
// Set patterns to exclude during formatting using regular expressions.
// For example, exclude all URLs.
config.excludePatterns.add("https?://\\S+".toRegex())
// For example, exclude emoji placeholders like "[doge]",
// if you use [ImageSpan] to display emoji images, you can choose to exclude these placeholders.
config.excludePatterns.add("\\[.*?]".toRegex())
// Set the spacing ratio for CJK characters.
// This determines the final layout effect.
// It is recommended to keep the default ratio and adjust it according to personal preference.
config.cjkSpacingRatio = 7f
```
::: warning
If you integrated using the [Inject to LayoutInflater](#inject-to-layoutinflater) method, configure `PanguText.globalConfig` before executing `PanguTextFactory2.inject(...)`, otherwise the configuration will not take effect.
:::
You can also pass the `config` parameter for personalized configuration when manually injecting or formatting text.
> The following example
```kotlin
// Assume this is your TextView.
val textView: TextView
// Create a new configuration.
// You can set [copyFromGlobal] to false to not copy from the global configuration.
val config = PanguTextConfig(copyFromGlobal = false) {
excludePatterns.add("https?://\\S+".toRegex())
excludePatterns.add("\\[.*?]".toRegex())
cjkSpacingRatio = 7f
}
// You can also copy and create a new configuration from any configuration.
val config2 = config.copy {
excludePatterns.clear()
excludePatterns.add("https?://\\S+".toRegex())
excludePatterns.add("\\[.*?]".toRegex())
cjkSpacingRatio = 7f
}
// Manually inject and configure.
textView.injectPanguText(config = config2)
```
If you integrated using the [Inject to LayoutInflater](#inject-to-layoutinflater) method, you can use the following attributes in the XML layout declaration of `TextView`, `EditText`, or their subclasses for personalized configuration.
- `panguText_enabled` corresponds to `PanguTextConfig.isEnabled`
- `panguText_processedSpanned` corresponds to `PanguTextConfig.isProcessedSpanned`
- `panguText_excludePatterns` corresponds to `PanguTextConfig.excludePatterns`, string array, multiple patterns separated by `|@|`
- `panguText_cjkSpacingRatio` corresponds to `PanguTextConfig.cjkSpacingRatio`
> The following example
```xml
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Xiaoming今年16岁"
app:panguText_enabled="true"
app:panguText_processedSpanned="true"
app:panguText_excludePatterns="https?://\\S+;\\[.*?]|@|\\[.*?]"
app:panguText_cjkSpacingRatio="7.0" />
```
::: warning
Due to issues with Android Studio, the above attributes may not have auto-completion hints. Please complete them manually.
Don't forget to add the declaration `xmlns:app="http://schemas.android.com/apk/res-auto"`.
:::
In custom `View`, you can extend your `View` to implement the `PanguTextView` interface to achieve the same functionality.
> The following example
```kotlin
class MyTextView(context: Context, attrs: AttributeSet? = null) : AppCompatTextView(context, attrs),
PanguTextView {
override fun configurePanguText(config: PanguTextConfig) {
// Configure your [PanguTextConfig].
}
}
```
::: warning
The `PanguTextView` interface takes precedence over attributes used directly in the XML layout. If you use both methods for configuration, the `PanguTextView` interface configuration will override the XML layout configuration.
Individual configurations will override global configurations, and options not configured will follow the global configuration.
:::

View File

@@ -0,0 +1,9 @@
# Jetpack Compose
![Maven Central](https://img.shields.io/maven-central/v/com.highcapable.pangutext/pangutext-compose?logo=apachemaven&logoColor=orange)
<span style="margin-left: 5px"/>
![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fraw.githubusercontent.com%2FHighCapable%2Fmaven-repository%2Frefs%2Fheads%2Fmain%2Frepository%2Freleases%2Fcom%2Fhighcapable%2Fpangutext%2Fpangutext-compose%2Fmaven-metadata.xml&logo=apachemaven&logoColor=orange&label=highcapable-maven-releases)
This is the core dependency for Jetpack Compose (multiplatform). When using `PanguText` in Jetpack Compose, you need to include this module.
This module is currently under development and will be gradually improved in the future.