diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index a340101..069bf68 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -1,43 +1,32 @@ -# Simple workflow for deploying static content to GitHub Pages name: Deploy static content to Pages on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - # workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages + push: + branches: ['main'] + workflow_dispatch: permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. + contents: read + pages: write + id-token: write concurrency: - group: "pages" - cancel-in-progress: false + group: 'pages' + cancel-in-progress: false jobs: - # Single deploy job since we're just deploying - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Pages - uses: actions/configure-pages@v5 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - # Upload entire repository - path: './pages' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: './pages' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.prettierignore b/.prettierignore index 22e8364..b216235 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,4 @@ -/* \ No newline at end of file +glist/ +pages/cglyphlist.js +pages/jglyphlist.js +pages/potrace.js \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d9384ec --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "useTabs": true, + "tabWidth": 4, + "singleQuote": true, + "semi": true, + "trailingComma": "es5", + "printWidth": 200, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/ChangeLog.md b/ChangeLog.md index 7b13108..1b511c0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,68 +2,68 @@ ## 0.594 - 2025/9/11 -- 若使用者手動新增自訂的全形字元,則該文字不自動產生全形字符。
+- 若使用者手動新增自訂的全形字元,則該文字不自動產生全形字符。 ユーザーが手動で追加した全角文字は自動的に全角グリフを生成しません。 --(僅日文版)新增括號類的直排字符(不知道為什麼多數環境不照UAX#50定義Tr旋轉)。
- (日本語版のみ)括弧類の縦組みグリフを追加(多くの環境でUAX#50のTr定義通りに回転しない理由は不明) +- (僅日文版)新增括號類的直排字符(不知道為什麼多數環境不照UAX#50定義Tr旋轉)。 +(日本語版のみ)括弧類の縦組みグリフを追加(多くの環境でUAX#50のTr定義通りに回転しない理由は不明) --- ## 0.59 - 2025/8/3 -- 增加背景格線選項。稍微降低歐文參考線位置。
+- 增加背景格線選項。稍微降低歐文參考線位置。 背景グリッドの選択肢を追加。欧文の参考線位置を少し下げました。 -- 解決 iOS Safari 按鈕點兩下容易不小心放大視窗的問題。
+- 解決 iOS Safari 按鈕點兩下容易不小心放大視窗的問題。 iOS Safari でボタンを2回タップすると画面が拡大されてしまう問題を修正。 ## 0.58 - 2025/8/3 -- 轉外框改為延遲執行,減少畫布操作時的延遲。(=解決iOS的斷水問題)
+- 轉外框改為延遲執行,減少畫布操作時的延遲。(=解決iOS的斷水問題) アウトライン変換を遅延実行に変更し、キャンバス操作時の遅延を軽減。(=iOSの描画中断問題を解決) -- 增加更增強的筆壓敏感度。
+- 增加更增強的筆壓敏感度。 さらに新しい筆圧感度を追加。 -- 減輕觸控筆下筆時容易產生圓球以及直線牽絲的問題。
+- 減輕觸控筆下筆時容易產生圓球以及直線牽絲的問題。 タッチペンで描画開始時に発生しやすい円形の点や直線の糸引き問題を軽減しました。 -- 全形字元改為採用原始位置。
+- 全形字元改為採用原始位置。 全角グリフの位置をオリジナル値に変更。 --- ## 0.57 - 2025/7/25 -- 類似 Procreate 的雙指快速碰觸復原功能。
+- 類似 Procreate 的雙指快速碰觸復原功能。 Procreate のような2本指でのタップによる復元機能。 -- 修復筆壓敏感度對於 Apple Pencil 過輕筆畫可能無法正常作畫的問題。
+- 修復筆壓敏感度對於 Apple Pencil 過輕筆畫可能無法正常作畫的問題。 Apple Pencil の筆圧が軽すぎる場合に描画が正常に行われない問題を修正しました。 --- ## 0.56 - 2025/7/23 -本日調整的功能均為實驗中,未來都可能還會微調,不保證相容性。
+本日調整的功能均為實驗中,未來都可能還會微調,不保證相容性。 本日調整した機能は全て実験中であり、変更する可能性アリ。互換性は保証できない。 -- 針對沒有筆壓感應的裝置,加上模擬筆壓功能。
+- 針對沒有筆壓感應的裝置,加上模擬筆壓功能。 筆圧非対応デバイスに対し、疑似的な筆圧機能を追加。 -- 對於真實筆壓裝置,加上筆壓敏感度設定。本功能應該可以解決Apple Pencil寫字要很用力的問題。
+- 對於真實筆壓裝置,加上筆壓敏感度設定。本功能應該可以解決Apple Pencil寫字要很用力的問題。 筆圧対応デバイスに対し、筆圧感度設定機能を追加。Apple Pencilの省力化。 --- ## 0.55 - 2025/7/22 -- 「我的字就是小」選項。
+- 「我的字就是小」選項。 「小さく書きたい!」オプション。 -- 介面設計微調(放大一些按鈕)
+- 介面設計微調(放大一些按鈕) UI微調整(ボタンサイズなど) -- 清除功能可以復原。
+- 清除功能可以復原。 クリア操作も復元可能に。 --- ## 0.54 - 2025/7/21 -- 自訂字符功能。
+- 自訂字符功能。 ユーザー文字リスト。 - 程式設定值讀取機制重構(本修改不影響功能,可提高程式可維護性) @@ -71,14 +71,14 @@ ## 0.52 - 2025/7/20 -- 修改一些事件捕捉機制,嘗試提高設備相容性。
+- 修改一些事件捕捉機制,嘗試提高設備相容性。 イベント処理の調整によりデバイスの互換性の向上。 -- 字型下載頁面獨立。
+- 字型下載頁面獨立。 フォントダウンロード機能のページ化。 ## 0.50 - 2025/7/20 -- 筆壓功能微調
+- 筆壓功能微調 筆圧対応の微調整。 - (日本語版)般若心経リスト追加。括弧系の縦組みグリフ廃止。 @@ -86,71 +86,81 @@ ## 0.48 - 2025/7/19 -- 支援筆刷切換,並支援感壓模式(不支援模擬,需要設備實際傳入筆壓值,如Apple Pencil,或支援手指感壓的裝置)
+- 支援筆刷切換,並支援感壓模式(不支援模擬,需要設備實際傳入筆壓值,如Apple Pencil,或支援手指感壓的裝置) ブラシ機能。筆圧に対応(筆圧対応のデバイスが必須)。 --- ## 0.40 - 2025/7/18 -- 支援鍵盤操作:
+ +- 支援鍵盤操作: キーボード操作をサポート: - - 允許使用者使用方向鍵移動字型。
- ユーザーが方向キーを使用して文字を移動できるようにしました。 - - `Z` 鍵復原、`V` 鍵切換畫筆、`C` 鍵切換橡皮擦、`X` 鍵清除。
- `Z` キーで元に戻す、`V` キーでペンに切り替え、`C` キーで消しゴムに切り替え、`X` キーでクリア。 - - `PageDown`、`]`、`Enter`、`Space` 鍵移至下個字。
- `PageDown`、`]`、`Enter`、`Space` キーで次の文字に移動。 - - `PageUp`、`[`、`Shift+Enter` 和 `Shift+Space` 鍵移至上個字。
- `PageUp`、`[`、`Shift+Enter`、`Shift+Space` キーで前の文字に移動。 + - 允許使用者使用方向鍵移動字型。 + ユーザーが方向キーを使用して文字を移動できるようにしました。 + - `Z` 鍵復原、`V` 鍵切換畫筆、`C` 鍵切換橡皮擦、`X` 鍵清除。 + `Z` キーで元に戻す、`V` キーでペンに切り替え、`C` キーで消しゴムに切り替え、`X` キーでクリア。 + - `PageDown`、`]`、`Enter`、`Space` 鍵移至下個字。 + `PageDown`、`]`、`Enter`、`Space` キーで次の文字に移動。 + - `PageUp`、`[`、`Shift+Enter` 和 `Shift+Space` 鍵移至上個字。 + `PageUp`、`[`、`Shift+Enter`、`Shift+Space` キーで前の文字に移動。 ## 0.39 - 2025/7/18 -- 修復非同步操作導致SVG最後筆畫消失問題。
+ +- 修復非同步操作導致SVG最後筆畫消失問題。 非同期操作による SVG の最後のストロークが消える問題を修正しました。 --- ## 0.38 - 2025/7/17 -- 字符列表改為顯示SVG資料,並縮放至產出字型時預期的大小。
+ +- 字符列表改為顯示SVG資料,並縮放至產出字型時預期的大小。 文字リストを SVG データを表示する形式に変更し、生成されるフォントの期待サイズにスケーリングしました。 -- 調整[說明文件](README.md)中關於授權、商標的相關註記。
+- 調整[說明文件](README.md)中關於授權、商標的相關註記。 [README.md](README.md) のライセンスおよび商標に関する注記を調整しました。 --- ## 0.37 - 2025/7/11 -- 調整筆壓模式。
+ +- 調整筆壓模式。 筆圧モードを微調整しました。 ## 0.35 - 2025/7/11 -- 版面調整(解決部分對話框出界問題)。
-レイアウトを調整し、一部のダイアログが画面外に出る問題を解決しました。 + +- 版面調整(解決部分對話框出界問題)。 + レイアウトを調整し、一部のダイアログが画面外に出る問題を解決しました。 - (中文版)增加台文全羅與心經附表。 ## 0.33 - 2025/7/11 -- 新增以測試模式輸出字型檔的功能,並作為預設值。
+ +- 新增以測試模式輸出字型檔的功能,並作為預設值。 テストモードによるフォント生成の機能を追加。これをデフォルト状態とします。 --- ## 0.32 - 2025/7/10 -- 新增筆壓模式。
+ +- 新增筆壓模式。 筆圧モードに対応しました。 ## 0.26 - 2025/7/10 -- 獨立此 Repository,並處理 Action 自動佈版機制。
+ +- 獨立此 Repository,並處理 Action 自動佈版機制。 GitHub Repositoryを分離。 --- ## 0.25 - 2025/7/9 -- 依照縮放率設定,調整字身框顯示大小。
+ +- 依照縮放率設定,調整字身框顯示大小。 拡大率の設定に従い、仮想ボディーの枠を表示します。 -- 橡皮擦功能。
+- 橡皮擦功能。 消しゴム機能。 - (日本語版)縦書き `uni308E.vert` グリフ追加。 --- ## 0.20 - 2025/7/8 -- 繁中版與日文版同時正式公開。
+ +- 繁中版與日文版同時正式公開。 繁体字中国語版と日本語版を同時に正式公開。 diff --git a/README.md b/README.md index ab2cd7e..7e327bb 100644 --- a/README.md +++ b/README.md @@ -17,40 +17,44 @@ FontDrawer 是一個基於 HTML5 Canvas 的字型繪製工具,允許使用者 ## 技術細節 - **主要技術**: - - HTML5 Canvas - - JavaScript - - IndexedDB 用於儲存字型資料 - - [potrace.js](https://github.com/kilobtye/potrace) (GPL 2.0授權)用於將繪製的圖像轉換為 SVG。 - - [opentype.js](https://github.com/opentypejs/opentype.js) (MIT授權)用於生成 OTF 字型檔案。 + + - HTML5 Canvas + - JavaScript + - IndexedDB 用於儲存字型資料 + - [potrace.js](https://github.com/kilobtye/potrace) (GPL 2.0授權)用於將繪製的圖像轉換為 SVG。 + - [opentype.js](https://github.com/opentypejs/opentype.js) (MIT授權)用於生成 OTF 字型檔案。 - **檔案結構**: - - `fontdrawer.js`:主要的字型繪製和生成邏輯。 - - `index.html`:繁體中文界面。 - - `ja.html`:日文界面。 - - `glist/`:用於產生字表的工具。 + - `fontdrawer.js`:主要的字型繪製和生成邏輯。 + - `index.html`:繁體中文界面。 + - `ja.html`:日文界面。 + - `glist/`:用於產生字表的工具。 ## 注意事項 - **字型權利**: - - 使用者生成的字型檔案的所有權屬於使用者,可自由公開或商業使用。並考慮斗內一下(笑) + + - 使用者生成的字型檔案的所有權屬於使用者,可自由公開或商業使用。並考慮斗內一下(笑) - **技術限制**: - - 生成的字型檔案可能不完全符合 CID 格式,部分 Adobe 應用可能無法正確識別為 CJK 字型。 + + - 生成的字型檔案可能不完全符合 CID 格式,部分 Adobe 應用可能無法正確識別為 CJK 字型。 - **建議**: - - 定期備份未完成的字型檔案,以防資料遺失。 + + - 定期備份未完成的字型檔案,以防資料遺失。 - **原始碼開源授權**: - - 本專案程式碼開源授權,但由於引用之專案授權條款不同,請自行評估引用時須遵守之各授權規範。並請勿侵害以下商標等權益: - - 網站名稱的「字嗨」為在台灣正式註冊的商標。 - - 產出之字型檔,VendorID所填入的「ZIHI」值,為本人於微軟註冊的字型廠商代碼。 + - 本專案程式碼開源授權,但由於引用之專案授權條款不同,請自行評估引用時須遵守之各授權規範。並請勿侵害以下商標等權益: + - 網站名稱的「字嗨」為在台灣正式註冊的商標。 + - 產出之字型檔,VendorID所填入的「ZIHI」值,為本人於微軟註冊的字型廠商代碼。 ## 貢獻者 - **開發者**:[@buttaiwan](https://x.com/buttaiwan) - **感謝**: - - [potrace.js](https://github.com/kilobtye/potrace) - - [opentype.js](https://github.com/opentypejs/opentype.js) + - [potrace.js](https://github.com/kilobtye/potrace) + - [opentype.js](https://github.com/opentypejs/opentype.js) ## 斗內 @@ -77,36 +81,40 @@ FontDrawer は、HTML5 Canvas を使用したフォント作成ツールで、 ## 技術詳細 - **主な技術**: - - HTML5 Canvas - - JavaScript - - IndexedDB を使用してフォントデータを保存 - - [potrace.js](https://github.com/kilobtye/potrace) (GPL 2.0ライセンス)を使用して描画した画像を SVG に変換。 - - [opentype.js](https://github.com/opentypejs/opentype.js) (MITライセンス)を使用して OTF フォントファイルを生成。 + + - HTML5 Canvas + - JavaScript + - IndexedDB を使用してフォントデータを保存 + - [potrace.js](https://github.com/kilobtye/potrace) (GPL 2.0ライセンス)を使用して描画した画像を SVG に変換。 + - [opentype.js](https://github.com/opentypejs/opentype.js) (MITライセンス)を使用して OTF フォントファイルを生成。 - **ファイル構成**: - - `fontdrawer.js`:フォント描画と生成の主要なロジック。 - - `index.html`:繁体字中国語インターフェース。 - - `ja.html`:日本語インターフェース。 - - `glist/`:文字表を生成するためのツール。 + - `fontdrawer.js`:フォント描画と生成の主要なロジック。 + - `index.html`:繁体字中国語インターフェース。 + - `ja.html`:日本語インターフェース。 + - `glist/`:文字表を生成するためのツール。 ## 注意事項 - **フォントの権利**: - - ユーザーが生成したフォントファイルのすべての権利はユーザーに帰属します。公開や商用利用も自由に行えます。もし気に入っていただけたら、寄付をお願いします(笑)。 + + - ユーザーが生成したフォントファイルのすべての権利はユーザーに帰属します。公開や商用利用も自由に行えます。もし気に入っていただけたら、寄付をお願いします(笑)。 - **技術的制限**: - - 生成されたフォントファイルは CID フォーマットに完全には準拠していないため、一部の Adobe アプリケーションでは CJK フォントとして正しく認識されない可能性があります。 + + - 生成されたフォントファイルは CID フォーマットに完全には準拠していないため、一部の Adobe アプリケーションでは CJK フォントとして正しく認識されない可能性があります。 - **推奨事項**: - - 未完成のフォントファイルは定期的にバックアップを行うことを推奨します。 + + - 未完成のフォントファイルは定期的にバックアップを行うことを推奨します。 - **オープンソースライセンス**: - - 本プロジェクトのコードはオープンソースライセンスで公開されていますが、引用されたプロジェクトのライセンス条項が異なるため、引用時には各ライセンス規約を遵守してください。また、以下の商標権などを侵害しないようご注意ください: - - ウェブサイト名「字嗨」は台湾で正式に登録された商標です。 - - 生成されたフォントファイルの VendorID に入力される「ZIHI」値は、Microsoft に登録された私個人のフォントベンダーコードです。 + - 本プロジェクトのコードはオープンソースライセンスで公開されていますが、引用されたプロジェクトのライセンス条項が異なるため、引用時には各ライセンス規約を遵守してください。また、以下の商標権などを侵害しないようご注意ください: + - ウェブサイト名「字嗨」は台湾で正式に登録された商標です。 + - 生成されたフォントファイルの VendorID に入力される「ZIHI」値は、Microsoft に登録された私個人のフォントベンダーコードです。 ## 寄付 もしこのツールが役に立ったと思われたら、ぜひ寄付をお願いします! -[PayPal](https://www.paypal.com/paypalme/buttaiwan) または [台湾ドル建てのクレジット決済](https://p.ecpay.com.tw/930AED7) が可能です。 \ No newline at end of file +[PayPal](https://www.paypal.com/paypalme/buttaiwan) または [台湾ドル建てのクレジット決済](https://p.ecpay.com.tw/930AED7) が可能です。 diff --git a/pages/contest.html b/pages/contest.html index b034bf7..d8ef251 100644 --- a/pages/contest.html +++ b/pages/contest.html @@ -1,142 +1,145 @@ - - - - 第一屆基礎字造句大賽 - - - - -

手寫字型產生器
第一屆基礎字造句大賽

- -
- -
- - - - - \ No newline at end of file + + + + + 第一屆基礎字造句大賽 + + + + +

手寫字型產生器
第一屆基礎字造句大賽

+ +
+ +
+ + + + + diff --git a/pages/fontdrawer.js b/pages/fontdrawer.js index 9f784be..0879285 100644 --- a/pages/fontdrawer.js +++ b/pages/fontdrawer.js @@ -1,7 +1,7 @@ const version = '0.595'; // 版本號 const upm = 1000; const userAgent = navigator.userAgent.toLowerCase(); -const pressureDelta = 1.3; // 筆壓模式跟一般模式的筆寬差異倍數 (舊筆壓模式用) +const pressureDelta = 1.3; // 筆壓模式跟一般模式的筆寬差異倍數 (舊筆壓模式用) const dbName = fdrawer.dbName || 'FontDrawerDB'; // 使用 fdrawer.dbName,如果未定義則使用預設值 const storeName = 'FontData'; const events = []; @@ -15,12 +15,24 @@ function addBrush(imgSrc) { brush.src = 'data:image/png;base64,' + imgSrc; brushes.push(brush); } -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFu2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6NDc6MTIrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6NDc6MTIrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjgyMTU3OTVjLTUwZDQtNzA0NC05NDczLTliY2FiOGEyNzdlNSIgeG1wTU06RG9jdW1lbnRJRD0i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'); -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAE7mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6Mjg6MTYrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6Mjg6MTYrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCIgeG1wTU06RG9jdW1lbnRJRD0i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'); -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU0YmZiNzZmLThmNGYtNGE0YS04OGEwLTc4MDA2NWI2ZjMwNCIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmY5ODYzOWYzLTU3YjMtZWU0Yy04OTUxLWM4MzAyYzAyNTk3YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjkyNGNkYWJlLWUxMmEtY2E0MS04YmE1LTEwODI2NmM4MzQzMiIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMTo1NTowMSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1NGJmYjc2Zi04ZjRmLTRhNGEtODhhMC03ODAwNjViNmYzMDQiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Ijiu+AAAKxklEQVR42u2d6U9cyRXFf73Q3ewMi8HLGLBhzGQyiRQpkUZRIuVz/uxkkoyU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'); -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg0ODE1NjE1LTNlOTktZTU0MC05MTJkLTIzOTIyNzU2NDdmYiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjMyNmMxZmViLWZhNTItZGI0ZC1iNmE1LWY3YTdkZmUxNTM3MiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc2YWQxMWJjLTU3YzYtOWE0YS1hOTQ5LWZmMTAzMWNmNDhmNCIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNToyMDo1NiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NDgxNTYxNS0zZTk5LWU1NDAtOTEyZC0yMzkyMjc1NjQ3ZmIiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7P7eCvAAAGsklEQVR42u2dbYiUVRTHf2d2XHPzZU1N0wKtzPd8Qcoy01Cx+mJl4IcyUJI+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'); -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjI3ZDI3MjBiLTUzYjgtMTM0NC04MGZjLTFkY2EwMzQxMzFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmU5ZWFjMDdmLTNmMDQtYzc0NS1iYTcxLTJlNjJkY2U5NmM5YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmFiZmViNmY4LTUwNzEtNWM0Ni04YjJkLTQxODYxYzE3NGIxYSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMjoyOTozMiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyN2QyNzIwYi01M2I4LTEzNDQtODBmYy0xZGNhMDM0MTMxZTYiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4oCt+pAAAQ7UlEQVR42u2dCXgURRbHKyQhAeQGRRBEoyI3inhwrCwKwnqh4okiAp6AriiH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'); -addBrush('iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg3MzBkNGY3LTY4ZmEtYjE0YS1iOGU3LTI2NjhhOTA2ZjU4MSIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmE2NzljNjU1LTU3NmItYmE0NS04NzhlLWQ5MGE4NTNhZjIwZSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjgyMTU3OTVjLTUwZDQtNzA0NC05NDczLTliY2FiOGEyNzdlNSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMTo0NzoxMiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NzMwZDRmNy02OGZhLWIxNGEtYjhlNy0yNjY4YTkwNmY1ODEiIHN0RXZ0OndoZW49IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6uWwLnAAAD8UlEQVR42u3cX8ieYxwH8M/DNtMWYzP1yghZkpPNn5lJW5KccCT/QkQSIUorESf+RJGwE8RqlDAndkBJsjkgm/yZhq0l2cZsjL0z2+3gvt71WM+7533+Tdu+37p66u05uj7PfV339bt/99uoqkry/+WwTEEAApAEIABJAAKQBCAASQACkAQgAEkAApAEIABJAAKQBCAASQACkAQgAEkAApD0L+P2/kOj0ZiDadiFYXyDbeW7WzJlvae5H7exd3Nuo9FYgVMwBT9gAn7GJ1iDb/EhdheUfzKl3QOoquo/A/djB6oWYyvW4Su8iqtwFoYyrZ0B7JnvFgCTsXAUgFbjO7xW4C7FpExxbwAjuRaryhIzFojtWFuWqscxK1PdG8DhOB/LOrgaRsau8rkGD2I2jsPETP/YAZpzb9l8qy7HTryLezD3UN8zugEYV5aUF7Gh3AF1i7EGr+MOnFfusgLQBmAkR+MafN4DwMjYhpVYjBsxIwCdvbB3H9b3AaLCxoLxNBYEYOw5AQ+V03I/ILbjN3yEO8uJPABtMrlsrC+U29CdfYAYuYvajJcwJwDtc2Q5iL2xj5N0L2MlbkUjAO2zoMvzw1jGDjyH0wPQPnPx1oAgKnyMywPQPvPKGeKXAUH8igdwVABap6Eub1+ERfh+gFfFklL6CMAomYaL8TxWd1Ds63Qsx9UBGD2Tymb9TIH4c0AQa9Xl9eMDMHrOxROlTrR9QBCb8CzODsDoObX8WleXgt/uPiPsLBCLcRnGB2D0Q91NWDGgq+Fv/IQ3ceX+Xp4OBIDmzMfbA7xr+hFL1U8ATwrA6JmBJ/HXgCA24p0CceIgl6cDFaA5t/SxAtuq1LGsQEwZRO3pYABoLnUsHeDytBw3BKB9jsXDpSQxCIh16qd2jQC0zxX4YEAQX+I2dYdHANpkJp5St1U2P+TpdWzF+7hOl90dhwrASMbherxXJm+4TxAbSuFvPo7oZHk61ACaMxuPqZ+ubepTIfB3dal9ZgDGnqnqxuKXC8bmPl0Vj+LkAHSWWbi9LCdf672Z4LNSRpkegM4yHRfiLvVj1C09QGxWt++fGYDuNu0hdYl8IT7tseh3t7qjMABdZEKpCZ1TNu9VpWa0rcPSxhKcEYD+5BI8Ukogqzs8xM0PQP8yhAtwM15Rd3wM4499IKzHgub5bvWSXqa2s4xXv8wyXt1uP0/9ZO80dUvlcLk7mlTOHV9UVbWnayMAgztnTMUx6jdOZ5SC4S6sr6pqUcsdOUtQ39P8ax5pGpu4zyUo2b/JvyoIQACSAAQgCUAAkgAEIAlAAJIABCAJQACSAAQgCUAAkgAEIAlAAJIABCAJwMGVfwFk+B+8bph6jgAAAABJRU5ErkJggg=='); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFu2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6NDc6MTIrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6NDc6MTIrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjgyMTU3OTVjLTUwZDQtNzA0NC05NDczLTliY2FiOGEyNzdlNSIgeG1wTU06RG9jdW1lbnRJRD0i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' +); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAE7mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6Mjg6MTYrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6Mjg6MTYrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCIgeG1wTU06RG9jdW1lbnRJRD0i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' +); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU0YmZiNzZmLThmNGYtNGE0YS04OGEwLTc4MDA2NWI2ZjMwNCIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmY5ODYzOWYzLTU3YjMtZWU0Yy04OTUxLWM4MzAyYzAyNTk3YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjkyNGNkYWJlLWUxMmEtY2E0MS04YmE1LTEwODI2NmM4MzQzMiIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMTo1NTowMSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1NGJmYjc2Zi04ZjRmLTRhNGEtODhhMC03ODAwNjViNmYzMDQiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTE6NTU6MDErMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Ijiu+AAAKxklEQVR42u2d6U9cyRXFf73Q3ewMi8HLGLBhzGQyiRQpkUZRIuVz/uxkkoyU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' +); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg0ODE1NjE1LTNlOTktZTU0MC05MTJkLTIzOTIyNzU2NDdmYiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjMyNmMxZmViLWZhNTItZGI0ZC1iNmE1LWY3YTdkZmUxNTM3MiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc2YWQxMWJjLTU3YzYtOWE0YS1hOTQ5LWZmMTAzMWNmNDhmNCIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNToyMDo1NiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NDgxNTYxNS0zZTk5LWU1NDAtOTEyZC0yMzkyMjc1NjQ3ZmIiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6MjA6NTYrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7P7eCvAAAGsklEQVR42u2dbYiUVRTHf2d2XHPzZU1N0wKtzPd8Qcoy01Cx+mJl4IcyUJI+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' +); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjI3ZDI3MjBiLTUzYjgtMTM0NC04MGZjLTFkY2EwMzQxMzFlNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmU5ZWFjMDdmLTNmMDQtYzc0NS1iYTcxLTJlNjJkY2U5NmM5YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmFiZmViNmY4LTUwNzEtNWM0Ni04YjJkLTQxODYxYzE3NGIxYSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMjoyOTozMiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoyN2QyNzIwYi01M2I4LTEzNDQtODBmYy0xZGNhMDM0MTMxZTYiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTI6Mjk6MzIrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4oCt+pAAAQ7UlEQVR42u2dCXgURRbHKyQhAeQGRRBEoyI3inhwrCwKwnqh4okiAp6AriiH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' +); +addBrush( + 'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg3MzBkNGY3LTY4ZmEtYjE0YS1iOGU3LTI2NjhhOTA2ZjU4MSIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmE2NzljNjU1LTU3NmItYmE0NS04NzhlLWQ5MGE4NTNhZjIwZSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjgyMTU3OTVjLTUwZDQtNzA0NC05NDczLTliY2FiOGEyNzdlNSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxMTo0NzoxMiswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NzMwZDRmNy02OGZhLWIxNGEtYjhlNy0yNjY4YTkwNmY1ODEiIHN0RXZ0OndoZW49IjIwMjUtMDctMjFUMjI6MDc6MTErMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6uWwLnAAAD8UlEQVR42u3cX8ieYxwH8M/DNtMWYzP1yghZkpPNn5lJW5KccCT/QkQSIUorESf+RJGwE8RqlDAndkBJsjkgm/yZhq0l2cZsjL0z2+3gvt71WM+7533+Tdu+37p66u05uj7PfV339bt/99uoqkry/+WwTEEAApAEIABJAAKQBCAASQACkAQgAEkAApAEIABJAAKQBCAASQACkAQgAEkAApD0L+P2/kOj0ZiDadiFYXyDbeW7WzJlvae5H7exd3Nuo9FYgVMwBT9gAn7GJ1iDb/EhdheUfzKl3QOoquo/A/djB6oWYyvW4Su8iqtwFoYyrZ0B7JnvFgCTsXAUgFbjO7xW4C7FpExxbwAjuRaryhIzFojtWFuWqscxK1PdG8DhOB/LOrgaRsau8rkGD2I2jsPETP/YAZpzb9l8qy7HTryLezD3UN8zugEYV5aUF7Gh3AF1i7EGr+MOnFfusgLQBmAkR+MafN4DwMjYhpVYjBsxIwCdvbB3H9b3AaLCxoLxNBYEYOw5AQ+V03I/ILbjN3yEO8uJPABtMrlsrC+U29CdfYAYuYvajJcwJwDtc2Q5iL2xj5N0L2MlbkUjAO2zoMvzw1jGDjyH0wPQPnPx1oAgKnyMywPQPvPKGeKXAUH8igdwVABap6Eub1+ERfh+gFfFklL6CMAomYaL8TxWd1Ds63Qsx9UBGD2Tymb9TIH4c0AQa9Xl9eMDMHrOxROlTrR9QBCb8CzODsDoObX8WleXgt/uPiPsLBCLcRnGB2D0Q91NWDGgq+Fv/IQ3ceX+Xp4OBIDmzMfbA7xr+hFL1U8ATwrA6JmBJ/HXgCA24p0CceIgl6cDFaA5t/SxAtuq1LGsQEwZRO3pYABoLnUsHeDytBw3BKB9jsXDpSQxCIh16qd2jQC0zxX4YEAQX+I2dYdHANpkJp5St1U2P+TpdWzF+7hOl90dhwrASMbherxXJm+4TxAbSuFvPo7oZHk61ACaMxuPqZ+ubepTIfB3dal9ZgDGnqnqxuKXC8bmPl0Vj+LkAHSWWbi9LCdf672Z4LNSRpkegM4yHRfiLvVj1C09QGxWt++fGYDuNu0hdYl8IT7tseh3t7qjMABdZEKpCZ1TNu9VpWa0rcPSxhKcEYD+5BI8Ukogqzs8xM0PQP8yhAtwM15Rd3wM4499IKzHgub5bvWSXqa2s4xXv8wyXt1uP0/9ZO80dUvlcLk7mlTOHV9UVbWnayMAgztnTMUx6jdOZ5SC4S6sr6pqUcsdOUtQ39P8ax5pGpu4zyUo2b/JvyoIQACSAAQgCUAAkgAEIAlAAJIABCAJQACSAAQgCUAAkgAEIAlAAJIABCAJwMGVfwFk+B+8bph6jgAAAABJRU5ErkJggg==' +); // 初始化 IndexedDB function initDB() { @@ -139,31 +151,32 @@ function clearDB() { // 讀取設定 async function loadSettings() { const settings = { - notNewFlag: await loadFromDB('notNewFlag', 'N') == 'Y', // 是否為舊檔案,預設為 N - scaleRate: await loadFromDB('scaleRate', 100) * 1, // 縮放比例,預設為 100% - smallMode: await loadFromDB('smallMode', 'N') == 'Y', // 是否小字模式,預設為 N - lineWidth: await loadFromDB('lineWidth', 12) * 1, // 筆寬,預設為 12 - brushType: await loadFromDB('brushType', 0) * 1, // 筆刷類型,預設為 0 - pressureMode: await loadFromDB('pressureMode', 'N') == 'Y', // 筆壓模式,預設為 N - pressureEffect: await loadFromDB('pressureEffect', 'none'), // 筆壓公式,預設為 none - gridType: await loadFromDB('gridType', '3x3grid'), // 格線類型,預設為 3x3grid - oldPressureMode: await loadFromDB('oldPressureMode', 'N') == 'Y', // 啟用舊版筆壓模式,預設為 N - fontNameEng: await loadFromDB('fontNameEng') || 'MyFreehandFont', - fontNameCJK: await loadFromDB('fontNameCJK') || fdrawer.fontNameCJK, - noFixedWidthFlag: await loadFromDB('noFixedWidthFlag', 'N') == 'Y', // 比例寬輸出,預設為 N - saveAsTester: await loadFromDB('saveAsTester', 'Y') == 'Y', // 是否為測試輸出,預設為 Y - testSerialNo: await loadFromDB('testSerialNo', 1) * 1, // 測試輸出序號,預設為 1 - customGlyphs: await loadFromDB('customGlyphs') // 自定義文字 + notNewFlag: (await loadFromDB('notNewFlag', 'N')) == 'Y', // 是否為舊檔案,預設為 N + scaleRate: (await loadFromDB('scaleRate', 100)) * 1, // 縮放比例,預設為 100% + smallMode: (await loadFromDB('smallMode', 'N')) == 'Y', // 是否小字模式,預設為 N + lineWidth: (await loadFromDB('lineWidth', 12)) * 1, // 筆寬,預設為 12 + brushType: (await loadFromDB('brushType', 0)) * 1, // 筆刷類型,預設為 0 + pressureMode: (await loadFromDB('pressureMode', 'N')) == 'Y', // 筆壓模式,預設為 N + pressureEffect: await loadFromDB('pressureEffect', 'none'), // 筆壓公式,預設為 none + gridType: await loadFromDB('gridType', '3x3grid'), // 格線類型,預設為 3x3grid + oldPressureMode: (await loadFromDB('oldPressureMode', 'N')) == 'Y', // 啟用舊版筆壓模式,預設為 N + fontNameEng: (await loadFromDB('fontNameEng')) || 'MyFreehandFont', + fontNameCJK: (await loadFromDB('fontNameCJK')) || fdrawer.fontNameCJK, + noFixedWidthFlag: (await loadFromDB('noFixedWidthFlag', 'N')) == 'Y', // 比例寬輸出,預設為 N + saveAsTester: (await loadFromDB('saveAsTester', 'Y')) == 'Y', // 是否為測試輸出,預設為 Y + testSerialNo: (await loadFromDB('testSerialNo', 1)) * 1, // 測試輸出序號,預設為 1 + customGlyphs: await loadFromDB('customGlyphs'), // 自定義文字 }; - if (settings.customGlyphs && settings.customGlyphs != '') { // 如果有自定義文字,則添加到列表中 + if (settings.customGlyphs && settings.customGlyphs != '') { + // 如果有自定義文字,則添加到列表中 var cglist = settings.customGlyphs.split(/,/); glyphList[fdrawer.customList] = []; for (var i = 0; i < cglist.length; i++) { glyphList[fdrawer.customList].push(cglist[i]); var uni = parseInt(cglist[i].replace(/^u(ni)?/g, ''), 16); - glyphMap[cglist[i]] = {c: String.fromCodePoint(uni), w :'F'}; // 將自定義文字添加到映射中 - } + glyphMap[cglist[i]] = { c: String.fromCodePoint(uni), w: 'F' }; // 將自定義文字添加到映射中 + } } console.log('Settings loaded:', settings); @@ -173,10 +186,10 @@ async function loadSettings() { async function updateSetting(key, value) { if (settings == null) settings = await loadSettings(); - if (typeof(value) != 'undefined') settings[key] = value; - if (typeof(settings[key]) == 'boolean') { + if (typeof value != 'undefined') settings[key] = value; + if (typeof settings[key] == 'boolean') { //console.log(`Updating setting ${key} to ${settings[key] ? 'Y' : 'N'}`); - await saveToDB(key, settings[key] ? 'Y' : 'N'); // 將布林值轉換為 'Y' 或 'N' + await saveToDB(key, settings[key] ? 'Y' : 'N'); // 將布林值轉換為 'Y' 或 'N' } else { //console.log(`Updating setting ${key} to ${settings[key]}`); await saveToDB(key, settings[key]); @@ -195,10 +208,10 @@ async function initCanvas(canvas) { const gridCtx = gridCanvas.getContext('2d'); gridCtx.clearRect(0, 0, gridCanvas.width, gridCanvas.height); - const emWidth = gridCanvas.width / scale; // 字身框寬度 - const emHeight = gridCanvas.height / scale; // 字身框高度 - const gridXOff = (gridCanvas.width - emWidth) / 2; // X 軸偏移量 - const gridYOff = (gridCanvas.height - emHeight) / 2; // X 軸偏移量 + const emWidth = gridCanvas.width / scale; // 字身框寬度 + const emHeight = gridCanvas.height / scale; // 字身框高度 + const gridXOff = (gridCanvas.width - emWidth) / 2; // X 軸偏移量 + const gridYOff = (gridCanvas.height - emHeight) / 2; // X 軸偏移量 //const gridWidth = Math.round(gridCanvas.width / scale / 3); // 每格寬度 //const gridHeight = Math.round(gridCanvas.height / scale / 3); // 每格高度 @@ -223,13 +236,13 @@ async function initCanvas(canvas) { if (settings.gridType == '3x3grid-new' && i == 2) continue; // 跳過新 3x3 格線的中間線 gridCtx.beginPath(); - gridCtx.moveTo(gridXOff + emWidth * i / lines, gridYOff); - gridCtx.lineTo(gridXOff + emWidth * i / lines, gridYOff + emHeight); + gridCtx.moveTo(gridXOff + (emWidth * i) / lines, gridYOff); + gridCtx.lineTo(gridXOff + (emWidth * i) / lines, gridYOff + emHeight); gridCtx.stroke(); gridCtx.beginPath(); - gridCtx.moveTo(gridXOff, gridYOff + emHeight * i / lines); - gridCtx.lineTo(gridXOff + emWidth, gridYOff + emHeight * i / lines); + gridCtx.moveTo(gridXOff, gridYOff + (emHeight * i) / lines); + gridCtx.lineTo(gridXOff + emWidth, gridYOff + (emHeight * i) / lines); gridCtx.stroke(); } @@ -244,7 +257,7 @@ async function initCanvas(canvas) { gridCtx.stroke(); } else if (settings.gridType == 'boxgrid') { gridCtx.beginPath(); - gridCtx.rect(gridXOff + emWidth*0.15, gridYOff + emHeight*0.15, emWidth*0.7, emHeight*0.7); + gridCtx.rect(gridXOff + emWidth * 0.15, gridYOff + emHeight * 0.15, emWidth * 0.7, emHeight * 0.7); gridCtx.stroke(); } @@ -269,25 +282,25 @@ async function initCanvas(canvas) { } // 繪製基線 - gridCtx.strokeStyle = '#ee9999'; // 基線顏色 + gridCtx.strokeStyle = '#ee9999'; // 基線顏色 gridCtx.beginPath(); - gridCtx.moveTo(0, gridYOff + emHeight*0.75); - gridCtx.lineTo(gridCanvas.width, gridYOff + emHeight*0.75); + gridCtx.moveTo(0, gridYOff + emHeight * 0.75); + gridCtx.lineTo(gridCanvas.width, gridYOff + emHeight * 0.75); gridCtx.stroke(); // 依照設定值顯示筆寬、筆刷、筆壓UI $('#lineWidthSlider').val(settings.lineWidth); $('#lineWidthValue').text(settings.lineWidth); $('#brushSelector').empty().append($(brushes[settings.brushType])); - $('#pressureButton').removeClass('on off').addClass(settings.pressureMode ? 'on' : 'off'); + $('#pressureButton') + .removeClass('on off') + .addClass(settings.pressureMode ? 'on' : 'off'); } function initListSelect($listSelect) { $listSelect.empty(); // 清空選單 for (var list in glyphList) { - $listSelect.append( - $('').val(list).text(list) - ); + $listSelect.append($('').val(list).text(list)); } } @@ -299,7 +312,7 @@ async function createFont(glyphs, gidMap, verts, ccmps) { //settings.fontNameCJK += settings.testSerialNo; updateSetting('testSerialNo', settings.testSerialNo + 1); // 更新測試序號 } - + const font = new opentype.Font({ familyName: settings.fontNameEng + testNo, fullName: settings.fontNameEng + testNo, @@ -313,7 +326,7 @@ async function createFont(glyphs, gidMap, verts, ccmps) { unitsPerEm: upm, ascender: 880, descender: -120, - glyphs: glyphs + glyphs: glyphs, }); for (var group in font.names) { @@ -338,7 +351,7 @@ async function createFont(glyphs, gidMap, verts, ccmps) { subfrom.push(gidMap[gname_from]); } if (!allpass) continue; - font.substitution.addLigature('ccmp', {sub: subfrom, by: gidMap[gname_to]}); + font.substitution.addLigature('ccmp', { sub: subfrom, by: gidMap[gname_to] }); } // verts @@ -347,7 +360,7 @@ async function createFont(glyphs, gidMap, verts, ccmps) { var gname_h = glyphMap[gname_v].v; if (!gidMap[gname_v]) continue; // 如果沒有對應的 cid,則跳過 if (!gidMap[gname_h]) continue; // 如果沒有對應的 cid,則跳過 - font.substitution.addSingle('vert', {sub: gidMap[gname_h], by: gidMap[gname_v]}); + font.substitution.addSingle('vert', { sub: gidMap[gname_h], by: gidMap[gname_v] }); } return font; @@ -356,40 +369,44 @@ async function createFont(glyphs, gidMap, verts, ccmps) { $(document).ready(async function () { const $listSelect = $('#listSelect'); - const $canvas = $('#drawingCanvas'); - const canvas = $canvas[0]; - const ctx = canvas.getContext('2d'); + const $canvas = $('#drawingCanvas'); + const canvas = $canvas[0]; + const ctx = canvas.getContext('2d'); let ratio = canvas.height / $canvas.height(); - let isDrawing = false; - const undoStack = []; // 儲存畫布狀態的堆疊 + let isDrawing = false; + const undoStack = []; // 儲存畫布狀態的堆疊 const $naviContainer = $('#navi-container'); const $progressContainer = $('#progress-container'); - const $progressBar = $('#progress-bar'); - const $progressText = $('#progress-text'); - - // 初始化 IndexedDB - initDB().then(async () => { - console.log('IndexedDB 起動完成'); - settings = await loadSettings(); - initListSelect($listSelect); - initCanvas(canvas); // 初始化九宮格底圖 - $('#canvas-container').toggleClass('smallmode', settings.smallMode); + const $progressBar = $('#progress-bar'); + const $progressText = $('#progress-text'); - $listSelect.change(); // 觸發一次 change 事件以載入第一個列表 - - // 初始化筆壓繪圖狀態 - await updatePressureDrawingStatus(); - - if (!settings.notNewFlag) { - $('#settingButton').click(); - } else { - if ($('#ads-container')) $('#ads-container').show(); - } - $('#spanDoneCount').text(await countGlyphFromDB()); + // 初始化 IndexedDB + initDB() + .then(async () => { + console.log('IndexedDB 起動完成'); + settings = await loadSettings(); + initListSelect($listSelect); + initCanvas(canvas); // 初始化九宮格底圖 + $('#canvas-container').toggleClass('smallmode', settings.smallMode); - }).catch((error) => { - console.error('IndexedDB 起動失敗', error); - }); + $listSelect.change(); // 觸發一次 change 事件以載入第一個列表 + + // 初始化筆壓繪圖狀態 + await updatePressureDrawingStatus(); + + if (!settings.notNewFlag) { + $('#settingButton').click(); + } else { + if ($('#ads-container')) $('#ads-container').show(); + } + $('#spanDoneCount').text(await countGlyphFromDB()); + + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.get('export') === 'true') await sendRightNOW(); + }) + .catch((error) => { + console.error('IndexedDB 起動失敗', error); + }); // (舊筆壓模式) 初始化 PressureDrawing 實例 const pressureDrawing = new PressureDrawing(); @@ -397,17 +414,17 @@ $(document).ready(async function () { let pressureDrawingSettings = { thinning: 0.5, smoothing: 0.4, - streamline: 0.4 + streamline: 0.4, }; // (舊筆壓模式) 更新筆壓繪圖狀態 async function updatePressureDrawingStatus() { const moduleInitialized = await pressureDrawing.initialize(); - + // 預設關閉筆壓繪圖 settings.oldPressureMode = settings.oldPressureMode && moduleInitialized; - $('#brushSelector').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆刷選擇器 - $('#pressureButton').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆壓開關 + $('#brushSelector').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆刷選擇器 + $('#pressureButton').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆壓開關 } let nowList = null; @@ -422,7 +439,7 @@ $(document).ready(async function () { nowGlyphIndex = 0; // 重置當前字形索引 setGlyph(0); } - }); //.change(); // 觸發一次 change 事件以載入第一個列表 + }); //.change(); // 觸發一次 change 事件以載入第一個列表 // 設定編輯中的字符 function setGlyph(index) { @@ -431,7 +448,7 @@ $(document).ready(async function () { if (index >= nowList.length) index = 0; // 如果索引大於字符數量,則設為第一個字符 nowGlyphIndex = index; nowGlyph = nowList[index]; // 取得當前字符的名稱 - + $('#glyphName').text(nowGlyph); // 更新顯示的字符 $('#charSeq').text(glyphMap[nowGlyph].c).removeClass('vert'); if (glyphMap[nowGlyph].v && nowGlyph.indexOf('.vert') > 0) $('#charSeq').addClass('vert'); @@ -442,7 +459,7 @@ $(document).ready(async function () { undoStack.length = 0; // 清空復原堆疊 ctx.clearRect(0, 0, canvas.width, canvas.height); loadCanvasData(nowGlyph); - + // 重置筆壓檢測狀態 if (settings.oldPressureMode) { pressureDrawing.resetPressureDetection(); @@ -456,7 +473,8 @@ $(document).ready(async function () { if (svgData && svgData != '') { await saveToDB('s_' + glyph, svgData); - } else { // 轉外框後才發現是空白的話,連同png一起清掉 + } else { + // 轉外框後才發現是空白的話,連同png一起清掉 await deleteFromDB('g_' + glyph); await deleteFromDB('s_' + glyph); } @@ -466,17 +484,20 @@ $(document).ready(async function () { // 儲存畫布的功能 async function saveToLocalDB(runNow = false) { - let saveGlyph = nowGlyph; // 嘗試解決非同步操作導致的 Race Condition + let saveGlyph = nowGlyph; // 嘗試解決非同步操作導致的 Race Condition const pngData = canvas.toDataURL(); await saveToDB('g_' + saveGlyph, pngData); - if (svgTimers[saveGlyph]) clearTimeout(svgTimers[saveGlyph]); // 清除之前的定時器 + if (svgTimers[saveGlyph]) clearTimeout(svgTimers[saveGlyph]); // 清除之前的定時器 - if (runNow) { // 如果立即儲存 - await saveSVG(saveGlyph, pngData); // 儲存 SVG - } else { // 延遲儲存 - svgTimers[saveGlyph] = setTimeout(async function () { // 延遲轉外框 - saveSVG(saveGlyph, pngData); // 儲存 SVG + if (runNow) { + // 如果立即儲存 + await saveSVG(saveGlyph, pngData); // 儲存 SVG + } else { + // 延遲儲存 + svgTimers[saveGlyph] = setTimeout(async function () { + // 延遲轉外框 + saveSVG(saveGlyph, pngData); // 儲存 SVG }, 1200); } } @@ -494,10 +515,14 @@ $(document).ready(async function () { } } - $('#prevButton').on('click', function () { setGlyph(nowGlyphIndex - 1); }); // 切換到上一個字符 - $('#nextButton').on('click', function () { setGlyph(nowGlyphIndex + 1); }); // 切換到下一個字符 + $('#prevButton').on('click', function () { + setGlyph(nowGlyphIndex - 1); + }); // 切換到上一個字符 + $('#nextButton').on('click', function () { + setGlyph(nowGlyphIndex + 1); + }); // 切換到下一個字符 - $('#findButton').on('click', function () { + $('#findButton').on('click', function () { var char = prompt(fdrawer.findMsg); if (!char) return; // 如果沒有輸入字符,則不進行任何操作 char = char.trim(); // 去除前後空白 @@ -508,8 +533,8 @@ $(document).ready(async function () { for (let j in glyphList[i]) { if (glyphMap[glyphList[i][j]].c == char) { nowList = glyphList[i]; - $listSelect.val(i); // 更新下拉選單的值 - setGlyph(j*1); + $listSelect.val(i); // 更新下拉選單的值 + setGlyph(j * 1); breakFlag = true; break; } @@ -524,31 +549,31 @@ $(document).ready(async function () { var uni = char.codePointAt(0).toString(16).toUpperCase(); var gn = uni.length <= 4 ? 'uni' + uni.padStart(4, '0') : 'u' + uni; // 生成 Unicode 名稱 var chr = String.fromCodePoint(char.codePointAt(0)); - + if (!glyphList[fdrawer.customList]) { glyphList[fdrawer.customList] = []; initListSelect($listSelect); // 重新初始化下拉選單 } glyphList[fdrawer.customList].push(gn); // 將新字符添加到自定義列表 - glyphMap[gn] = {c: chr, w :'F'}; // 將自定義文字添加到映射中 + glyphMap[gn] = { c: chr, w: 'F' }; // 將自定義文字添加到映射中 updateSetting('customGlyphs', glyphList[fdrawer.customList].join(',')); // 儲存自定義字符 - + nowList = glyphList[fdrawer.customList]; - $listSelect.val(fdrawer.customList); // 更新下拉選單的值 - setGlyph(glyphList[fdrawer.customList].length-1); + $listSelect.val(fdrawer.customList); // 更新下拉選單的值 + setGlyph(glyphList[fdrawer.customList].length - 1); } } else { alert(fdrawer.notFound); } } - }); + }); - // 更新筆寬 - $('#lineWidthSlider').on('input', function () { - settings.lineWidth = parseInt($(this).val(), 10); - $('#lineWidthValue').text(settings.lineWidth); - updateSetting('lineWidth'); // 儲存筆寬到 Local Storage - }); + // 更新筆寬 + $('#lineWidthSlider').on('input', function () { + settings.lineWidth = parseInt($(this).val(), 10); + $('#lineWidthValue').text(settings.lineWidth); + updateSetting('lineWidth'); // 儲存筆寬到 Local Storage + }); // 切換筆刷 $('#brushSelector').on('click', function () { @@ -563,7 +588,9 @@ $(document).ready(async function () { $('#pressureButton').on('click', function () { settings.pressureMode = !settings.pressureMode; updateSetting('pressureMode'); - $('#pressureButton').removeClass('on off').addClass(settings.pressureMode ? 'on' : 'off'); + $('#pressureButton') + .removeClass('on off') + .addClass(settings.pressureMode ? 'on' : 'off'); }); // 切換畫筆與橡皮擦模式 @@ -572,16 +599,16 @@ $(document).ready(async function () { $('#eraserButton').removeClass('use'); eraseMode = false; }); - $('#eraserButton').on('click', function () { + $('#eraserButton').on('click', function () { $('#eraserButton').addClass('use'); $('#penButton').removeClass('use'); eraseMode = true; // 切換到橡皮擦模式 }); - let hasPointerEvent = false; // 這個筆畫是否有pointer事件 - let hasRealPressure = false; // 這個筆畫是否曾經有疑似真實的筆壓值 - let simulatePressure = false; // 模擬筆壓開關 - let lastPressure = 0.5; // 上一次的筆壓值 + let hasPointerEvent = false; // 這個筆畫是否有pointer事件 + let hasRealPressure = false; // 這個筆畫是否曾經有疑似真實的筆壓值 + let simulatePressure = false; // 模擬筆壓開關 + let lastPressure = 0.5; // 上一次的筆壓值 function getPressureValue(mode, event, x, y) { if (!settings.pressureMode) return 0.5; @@ -592,54 +619,56 @@ $(document).ready(async function () { let toolType = event.originalEvent.pointerType; let pressure = event.originalEvent.pressure; let touchForce = event.originalEvent.touches && event.originalEvent.touches.length > 0 ? event.originalEvent.touches[0].force : null; - let webkitForce = event.originalEvent.webkitForce !== undefined ? event.originalEvent.webkitForce : null; + let webkitForce = event.originalEvent.webkitForce !== undefined ? event.originalEvent.webkitForce : null; if (mode == 'start') { if (events.length > 1000) events.splice(0, events.length - 200); - if (eventType.includes('pointer')) hasPointerEvent = true; // 這個筆畫有pointer事件 + if (eventType.includes('pointer')) hasPointerEvent = true; // 這個筆畫有pointer事件 } else if (mode == 'end') { simulatePressure = false; hasTouchEvent = false; hasRealPressure = false; hasPointerEvent = false; } - + events.push(`${mode} - ${eventType} / ${toolType} / P:${pressure} / T:${touchForce} / W:${webkitForce}`); // 儲存事件資訊 //console.log(`${eventType} / ${toolType} / P:${pressure} / T:${touchForce} / W:${webkitForce}`); // 儲存事件資訊 - let isRealPressure = typeof(pressure) != 'undefined'; + let isRealPressure = typeof pressure != 'undefined'; if (isRealPressure && toolType != 'pen' && pressure == 0) isRealPressure = false; if (isRealPressure && toolType != 'pen' && !hasRealPressure && (pressure == 1 || pressure == 0.5)) isRealPressure = false; if (toolType == 'pen' && pressure < 0.01) return null; if (mode != 'start' && !simulatePressure && !isRealPressure) return null; if (isRealPressure) { - simulatePressure = false; // 如果移動中發現有真實筆壓值,則關閉模擬筆壓 - if (pressure > 0 && pressure < 1 && pressure != 0.5) hasRealPressure = true; // 出現過看似真實的筆壓值 + simulatePressure = false; // 如果移動中發現有真實筆壓值,則關閉模擬筆壓 + if (pressure > 0 && pressure < 1 && pressure != 0.5) hasRealPressure = true; // 出現過看似真實的筆壓值 // 真實筆壓值套用敏感度運算 - if (settings.pressureEffect == 'contrast') pressure = 0.5 + Math.sin((pressure*0.9-0.45) * Math.PI)/2; - if (settings.pressureEffect == 'enhance') pressure = Math.sin(pressure * Math.PI / 2); - if (settings.pressureEffect == 'enhancex') pressure = Math.sin(Math.sin(pressure * Math.PI / 2) * Math.PI / 2); + if (settings.pressureEffect == 'contrast') pressure = 0.5 + Math.sin((pressure * 0.9 - 0.45) * Math.PI) / 2; + if (settings.pressureEffect == 'enhance') pressure = Math.sin((pressure * Math.PI) / 2); + if (settings.pressureEffect == 'enhancex') pressure = Math.sin((Math.sin((pressure * Math.PI) / 2) * Math.PI) / 2); if (mode != 'start') pressure = (lastPressure + pressure) / 2; - return lastPressure = pressure; - + return (lastPressure = pressure); } else if (mode == 'start') { - simulatePressure = true; // 如果開始繪製時沒有真實筆壓值,則開啟模擬筆壓 + simulatePressure = true; // 如果開始繪製時沒有真實筆壓值,則開啟模擬筆壓 return 0.5; - - } else { //if (simulatePressure && lastX && lastY) { + } else { + //if (simulatePressure && lastX && lastY) { let distance = Math.sqrt(Math.pow(x - lastX, 2) + Math.pow(y - lastY, 2)); let speedFactor = Math.min(1, 5 / Math.max(distance, 1)); - pressure = (lastPressure*3 + speedFactor * 0.65 + 0.05) / 4; - return lastPressure = pressure; + pressure = (lastPressure * 3 + speedFactor * 0.65 + 0.05) / 4; + return (lastPressure = pressure); } } - // 儲存背景用於筆壓繪圖的即時預覽 - let backgroundImageData = null; - let lastX, lastY, lastLW, isMoved = false; + // 儲存背景用於筆壓繪圖的即時預覽 + let backgroundImageData = null; + let lastX, + lastY, + lastLW, + isMoved = false; var eraseMode = false; function drawBrush(ctx, brush, x, y, lw) { @@ -652,18 +681,18 @@ $(document).ready(async function () { brushCanvas.height = lw; const brushCtx = brushCanvas.getContext('2d'); brushCtx.drawImage(brush, 0, 0, lw, lw); - - ctx.drawImage(brushCanvas, x - lw/2, y - lw/2); + + ctx.drawImage(brushCanvas, x - lw / 2, y - lw / 2); } else { // 其他瀏覽器直接繪製 - ctx.drawImage(brush, x - lw/2, y - lw/2, lw+1, lw+1); + ctx.drawImage(brush, x - lw / 2, y - lw / 2, lw + 1, lw + 1); } } - // 開始繪製 + // 開始繪製 $canvas.on('mousedown touchstart pointerdown', function (event) { if (event.touches && event.touches.length === 2) { - if (isDrawing) $('#undoButton').trigger('click'); // 先撤銷掉目前的筆劃 + if (isDrawing) $('#undoButton').trigger('click'); // 先撤銷掉目前的筆劃 isDrawing = false; return; } @@ -672,72 +701,73 @@ $(document).ready(async function () { const { x, y } = getCanvasCoordinates(event); var pressureVal = getPressureValue('start', event, x, y); - ratio = canvas.height / $canvas.height(); // 筆畫開始時重新確認一次螢幕縮放比(因為有可能調過視窗大小等) + ratio = canvas.height / $canvas.height(); // 筆畫開始時重新確認一次螢幕縮放比(因為有可能調過視窗大小等) var png = canvas.toDataURL(); - if (!isDrawing && png != undoStack[undoStack.length-1]) undoStack.push(png); // 儲存當前畫布狀態到 undoStack - isDrawing = true; // 儲存畫布後正式宣告筆畫開始 - if (svgTimers[nowGlyph]) clearTimeout(svgTimers[nowGlyph]); // 停止SVG轉外框 (提高效能) - - if (settings.oldPressureMode) { // 舊筆壓模式 - const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'start'); - pressureDrawing.startStroke(x * ratio, y * ratio, pressure); - backgroundImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // 儲存背景圖像用於即時預覽 - - // 防止預設的觸控行為(如滾動) - event.preventDefault(); - - } else { // 筆刷模式 + if (!isDrawing && png != undoStack[undoStack.length - 1]) undoStack.push(png); // 儲存當前畫布狀態到 undoStack + isDrawing = true; // 儲存畫布後正式宣告筆畫開始 + if (svgTimers[nowGlyph]) clearTimeout(svgTimers[nowGlyph]); // 停止SVG轉外框 (提高效能) + + if (settings.oldPressureMode) { + // 舊筆壓模式 + const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'start'); + pressureDrawing.startStroke(x * ratio, y * ratio, pressure); + backgroundImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // 儲存背景圖像用於即時預覽 + + // 防止預設的觸控行為(如滾動) + event.preventDefault(); + } else { + // 筆刷模式 var lw = settings.lineWidth * pressureVal * 2; // 計算線寬 - ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over + ctx.globalCompositeOperation = eraseMode ? 'destination-out' : 'source-over'; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over //if (event.type.includes('pointer')) //drawBrush(ctx, brushes[settings.brushType], x*ratio, y*ratio, lw); lastX = x; // 儲存最後的 X 座標 lastY = y; // 儲存最後的 Y 座標 - lastLW = lw; + lastLW = lw; isMoved = false; - } + } }); - // 繪製中 + // 繪製中 $canvas.on('mousemove touchmove pointermove', function (event) { if (!isDrawing) return; - const { x, y } = getCanvasCoordinates(event); + const { x, y } = getCanvasCoordinates(event); var pressureVal = getPressureValue('move', event, x, y); - if (settings.pressureMode && pressureVal == null) return; // 筆壓模式必須要有筆壓值 - - if (settings.oldPressureMode) { // 舊筆壓模式 - // 使用筆壓繪圖系統:收集點並提供即時預覽 - const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'move'); - pressureDrawing.addPoint(x * ratio, y * ratio, pressure); - - // 生成即時預覽筆跡 + if (settings.pressureMode && pressureVal == null) return; // 筆壓模式必須要有筆壓值 + + if (settings.oldPressureMode) { + // 舊筆壓模式 + // 使用筆壓繪圖系統:收集點並提供即時預覽 + const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'move'); + pressureDrawing.addPoint(x * ratio, y * ratio, pressure); + + // 生成即時預覽筆跡 pressureDrawingSettings.size = settings.lineWidth * pressureDelta; - const previewStroke = pressureDrawing.createPreviewStroke(pressureDrawingSettings); - if (previewStroke && backgroundImageData) { - ctx.putImageData(backgroundImageData, 0, 0); // 恢復背景圖像 - pressureDrawing.drawStrokeOnCanvas(ctx, previewStroke, eraseMode); // 繪製預覽筆跡 - } - - // 防止預設的觸控行為 - event.preventDefault(); + const previewStroke = pressureDrawing.createPreviewStroke(pressureDrawingSettings); + if (previewStroke && backgroundImageData) { + ctx.putImageData(backgroundImageData, 0, 0); // 恢復背景圖像 + pressureDrawing.drawStrokeOnCanvas(ctx, previewStroke, eraseMode); // 繪製預覽筆跡 + } + // 防止預設的觸控行為 + event.preventDefault(); } else { - ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over + ctx.globalCompositeOperation = eraseMode ? 'destination-out' : 'source-over'; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over var lw = settings.lineWidth * pressureVal * 2; var d = Math.max(Math.abs(lastX - x), Math.abs(lastY - y)) * 1.5; if (d > 40) events.push(`Long-DrawImage / ${pressureVal} / ${event.originalEvent.pointerType} / ${x}, ${y}, ${lw} (${lastX}, ${lastY}, ${lastLW}) ${d}`); // 儲存事件資訊 if (d > 0) { - for (var t = d; t>0; t--) { - var tx = (lastX + (x - lastX) * t / d) * ratio; - var ty = (lastY + (y - lastY) * t / d) * ratio; - var tlw = lastLW + (lw - lastLW) * t / d; // 線寬漸變 + for (var t = d; t > 0; t--) { + var tx = (lastX + ((x - lastX) * t) / d) * ratio; + var ty = (lastY + ((y - lastY) * t) / d) * ratio; + var tlw = lastLW + ((lw - lastLW) * t) / d; // 線寬漸變 - drawBrush(ctx, brushes[settings.brushType], tx, ty, tlw); - } + drawBrush(ctx, brushes[settings.brushType], tx, ty, tlw); + } events.push(`Move-DrawImage / ${pressureVal} / ${event.originalEvent.pointerType} / ${x}, ${y}, ${lw} (${lastX}, ${lastY}, ${lastLW}) ${d}`); // 儲存事件資訊 } @@ -745,78 +775,82 @@ $(document).ready(async function () { lastY = y; // 更新最後的 Y 座標 lastLW = lw; // 更新最後的筆寬 isMoved = true; - } - }); + } + }); - // 停止繪製 - $canvas.on('mouseup mouseleave touchend pointerup pointerleave', function (event) { - if (!isDrawing) return; - isDrawing = false; + // 停止繪製 + $canvas.on('mouseup mouseleave touchend pointerup pointerleave', function (event) { + if (!isDrawing) return; + isDrawing = false; const { x, y } = getCanvasCoordinates(event); getPressureValue('end', event, x, y); - if (settings.oldPressureMode) { // 舊筆壓模式 - // 使用筆壓繪圖系統:生成最終筆跡並繪製 + if (settings.oldPressureMode) { + // 舊筆壓模式 + // 使用筆壓繪圖系統:生成最終筆跡並繪製 pressureDrawingSettings.size = settings.lineWidth * pressureDelta; - const finalStroke = pressureDrawing.finishStroke(pressureDrawingSettings); - if (finalStroke && finalStroke.length > 0) { - if (backgroundImageData) ctx.putImageData(backgroundImageData, 0, 0); // 恢復背景圖像(如果有的話) - pressureDrawing.drawStrokeOnCanvas(ctx, finalStroke, eraseMode); // 繪製最終筆跡 - } - - // 清除背景圖像數據 - backgroundImageData = null; - } else { + const finalStroke = pressureDrawing.finishStroke(pressureDrawingSettings); + if (finalStroke && finalStroke.length > 0) { + if (backgroundImageData) ctx.putImageData(backgroundImageData, 0, 0); // 恢復背景圖像(如果有的話) + pressureDrawing.drawStrokeOnCanvas(ctx, finalStroke, eraseMode); // 繪製最終筆跡 + } + + // 清除背景圖像數據 + backgroundImageData = null; + } else { if (!isMoved) { - ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over - drawBrush(ctx, brushes[settings.brushType], lastX*ratio, lastY*ratio, lastLW); + ctx.globalCompositeOperation = eraseMode ? 'destination-out' : 'source-over'; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over + drawBrush(ctx, brushes[settings.brushType], lastX * ratio, lastY * ratio, lastLW); } lastX = null; lastY = null; lastLW = null; isMoved = false; // 重置移動狀態 - } - - ctx.globalCompositeOperation = "source-over"; // 恢復正常繪圖模式(重要) - - saveToLocalDB(); // 停止繪製時儲存畫布內容到 Local Storage - }); - - // 復原功能 - $('#undoButton').on('click', function () { - if (undoStack.length > 0) { - const lastState = undoStack.pop(); - const img = new Image(); - img.src = lastState; - img.onload = function () { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0); - saveToLocalDB(); // 復原後更新 Local Storage - }; - } - }); - - // 雙指復原 - let undoTouchTime = null; - $(document).on('touchstart', function (event) { - if (event.touches.length === 2) { - undoTouchTime = new Date().getTime(); // 記錄雙指觸控的時間 } - }).on('touchend', function (event) { - if (undoTouchTime && new Date().getTime() - undoTouchTime < 250) { // 如果雙指觸控時間夠短 - $('#undoButton').trigger('click'); - undoTouchTime = null; + + ctx.globalCompositeOperation = 'source-over'; // 恢復正常繪圖模式(重要) + + saveToLocalDB(); // 停止繪製時儲存畫布內容到 Local Storage + }); + + // 復原功能 + $('#undoButton').on('click', function () { + if (undoStack.length > 0) { + const lastState = undoStack.pop(); + const img = new Image(); + img.src = lastState; + img.onload = function () { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + saveToLocalDB(); // 復原後更新 Local Storage + }; } }); + // 雙指復原 + let undoTouchTime = null; + $(document) + .on('touchstart', function (event) { + if (event.touches.length === 2) { + undoTouchTime = new Date().getTime(); // 記錄雙指觸控的時間 + } + }) + .on('touchend', function (event) { + if (undoTouchTime && new Date().getTime() - undoTouchTime < 250) { + // 如果雙指觸控時間夠短 + $('#undoButton').trigger('click'); + undoTouchTime = null; + } + }); + // 清除畫布的功能 $('#clearButton').on('click', async function () { const savedCanvas = await loadFromDB('g_' + nowGlyph); if (!savedCanvas) return; // 如果沒有儲存的畫布,則不進行任何操作 undoStack.push(canvas.toDataURL()); // 儲存當前畫布狀態到 undoStack ctx.clearRect(0, 0, canvas.width, canvas.height); - + //undoStack.length = 0; // 清空復原堆疊 await deleteFromDB('g_' + nowGlyph); // 清除 IndexedDB 中的資料 await deleteFromDB('s_' + nowGlyph); // 清除 IndexedDB 中的資料 @@ -836,10 +870,18 @@ $(document).ready(async function () { }; } - $('#moveLeftButton').on('click', function () { moveGlyph(-10, 0); }); // 向左移動 10px - $('#moveRightButton').on('click', function () { moveGlyph(10, 0); }); // 向右移動 10px - $('#moveUpButton').on('click', function () { moveGlyph(0, -10); }); // 向上移動 10px - $('#moveDownButton').on('click', function () { moveGlyph(0, 10); }); // 向下移動 10px + $('#moveLeftButton').on('click', function () { + moveGlyph(-10, 0); + }); // 向左移動 10px + $('#moveRightButton').on('click', function () { + moveGlyph(10, 0); + }); // 向右移動 10px + $('#moveUpButton').on('click', function () { + moveGlyph(0, -10); + }); // 向上移動 10px + $('#moveDownButton').on('click', function () { + moveGlyph(0, 10); + }); // 向下移動 10px // 支援鍵盤方向鍵操作 $(document).on('keydown', function (event) { @@ -874,27 +916,27 @@ $(document).ready(async function () { case 'n': // N 鍵 - 切換筆壓 $('#pressureButton').trigger('click'); break; - case 'PageDown': // PageDown 鍵 - 下一步 - case ']': // "]" 鍵 - 下一步 + case 'PageDown': // PageDown 鍵 - 下一步 + case ']': // "]" 鍵 - 下一步 $('#nextButton').trigger('click'); break; - case 'PageUp': // PageUp 鍵 - 上一步 - case '[': // "]" 鍵 - 下一步 + case 'PageUp': // PageUp 鍵 - 上一步 + case '[': // "]" 鍵 - 下一步 $('#prevButton').trigger('click'); break; - case 'Enter': // Enter 鍵 - 下一步 / 同時按shift - 上一步 - case ' ': // Space 鍵 - 下一步 / 同時按shift - 上一步 + case 'Enter': // Enter 鍵 - 下一步 / 同時按shift - 上一步 + case ' ': // Space 鍵 - 下一步 / 同時按shift - 上一步 $(event.shiftKey ? '#prevButton' : '#nextButton').trigger('click'); break; } }); - // 更新進度條 - function updateProgress(current, total) { - const percentage = Math.round((current / total) * 100); - $progressBar.val(percentage); - $progressText.text(`${percentage}%`); - } + // 更新進度條 + function updateProgress(current, total) { + const percentage = Math.round((current / total) * 100); + $progressBar.val(percentage); + $progressText.text(`${percentage}%`); + } async function toSVG(gname, savedCanvas) { // 建立一個臨時的 canvas @@ -928,7 +970,7 @@ $(document).ready(async function () { async function loadSVG(gname) { var savedSvg = await loadFromDB('s_' + gname); - if (savedSvg) return savedSvg; // 如果已經存在 SVG,則直接返回 + if (savedSvg) return savedSvg; // 如果已經存在 SVG,則直接返回 var savedCanvas = await loadFromDB('g_' + gname); if (!savedCanvas) return null; @@ -941,7 +983,7 @@ $(document).ready(async function () { var glyphObj = { name: gname, advanceWidth: adw, - path: path || new opentype.Path() + path: path || new opentype.Path(), }; if (unicode) glyphObj.unicode = unicode; return new opentype.Glyph(glyphObj); @@ -950,14 +992,14 @@ $(document).ready(async function () { function padPath(path, pad) { var boundingBox = path.getBoundingBox(); var width = Math.round(boundingBox.x2 - boundingBox.x1); - var xoff = pad - Math.round(boundingBox.x1); // 單純指定邊界寬度 + var xoff = pad - Math.round(boundingBox.x1); // 單純指定邊界寬度 - path.commands.forEach( c => { + path.commands.forEach((c) => { c.x = c.x + xoff; if (c.x1) c.x1 = c.x1 + xoff; if (c.x2) c.x2 = c.x2 + xoff; }); - return width + pad*2; // 返回調整後的寬度 + return width + pad * 2; // 返回調整後的寬度 } $('#saveAsTester').on('click', async function () { @@ -965,21 +1007,23 @@ $(document).ready(async function () { }); // 儲存字型檔 - $('#downloadFontButton').on('click', async function () { + + async function loadWriting() { // 顯示進度條 $naviContainer.hide(); $progressContainer.show(); $progressBar.val(0); $progressText.text('0%'); - const glyphs = [ // 建立字符陣列,並加入一些空格字符(因程式機制上無法畫出空白字符,只能自動產生) - createGlyph(null, '.notdef', 600), // notdef - createGlyph(0x20, 'space', 300), // 空格 - createGlyph(0xA0, 'uni00A0', 300), // No-break Space - createGlyph(0x2c9, 'macron', 600), // 一聲 - createGlyph(0x3000, 'uni3000', upm), // Ideographic Space - createGlyph(0x2002, 'uni2002', upm/2), // En Space - createGlyph(0x2003, 'uni2003', upm), // Em Space + const glyphs = [ + // 建立字符陣列,並加入一些空格字符(因程式機制上無法畫出空白字符,只能自動產生) + createGlyph(null, '.notdef', 600), // notdef + createGlyph(0x20, 'space', 300), // 空格 + createGlyph(0xa0, 'uni00A0', 300), // No-break Space + createGlyph(0x2c9, 'macron', 600), // 一聲 + createGlyph(0x3000, 'uni3000', upm), // Ideographic Space + createGlyph(0x2002, 'uni2002', upm / 2), // En Space + createGlyph(0x2003, 'uni2003', upm), // Em Space ]; const gidMap = {}; @@ -994,16 +1038,22 @@ $(document).ready(async function () { for (let gname in glyphMap) { // 更新進度條 - updateProgress(processedGlyphs, totalGlyphs); + updateProgress(processedGlyphs, totalGlyphs); processedGlyphs++; try { - let svgData = await loadSVG(gname); + let svgData = await loadSVG(gname); if (!svgData) continue; - let path = await opentype.Path.fromSVG(svgData, {flipYBase: 0, scale: scale, y: 880 - scaleoff, x: scaleoff}); + let path = await opentype.Path.fromSVG(svgData, { + flipYBase: 0, + scale: scale, + y: 880 - scaleoff, + x: scaleoff, + }); let adw = upm; - if (glyphMap[gname].w == 'P' || glyphMap[gname].w == 'H') { // 比例寬自動調整 + if (glyphMap[gname].w == 'P' || glyphMap[gname].w == 'H') { + // 比例寬自動調整 adw = padPath(path, 50); } else if (settings.noFixedWidthFlag) { adw = padPath(path, 100); @@ -1019,12 +1069,17 @@ $(document).ready(async function () { } let glyph = createGlyph(unicode, gname, adw, path); glyphs.push(glyph); - gidMap[gname] = glyphs.length-1; + gidMap[gname] = glyphs.length - 1; // 自動製作全形字符 if (glyphMap[gname].f) { let gnameF = glyphMap[gname].f; - let pathF = await opentype.Path.fromSVG(svgData, {flipYBase: 0, scale: scale, y: 880 - scaleoff, x: scaleoff}); + let pathF = await opentype.Path.fromSVG(svgData, { + flipYBase: 0, + scale: scale, + y: 880 - scaleoff, + x: scaleoff, + }); let adwF = upm; if (settings.noFixedWidthFlag) adwF = padPath(pathF, 100); // 如果沒有固定寬度 let unicodeF = null; @@ -1043,16 +1098,22 @@ $(document).ready(async function () { // 加入全形字符在後面 for (let i in fulls) { var glyphF = fulls[i]; - if (gidMap[glyphF.name]) continue; // 如果使用者已經自行繪製全形字符,則跳過 + if (gidMap[glyphF.name]) continue; // 如果使用者已經自行繪製全形字符,則跳過 glyphs.push(glyphF); - gidMap[glyphF.name] = glyphs.length-1; - } + gidMap[glyphF.name] = glyphs.length - 1; + } const font = await createFont(glyphs, gidMap, verts, ccmps); - + return font; + } + // 儲存字型檔 + $('#downloadFontButton').on('click', async function () { + const font = await loadWriting(); // 建立下載連結 const link = document.createElement('a'); link.download = font.names.windows.postScriptName.en + '.otf'; //'drawing.otf'; - link.href = window.URL.createObjectURL(new Blob([font.toArrayBuffer()]), {type: "font/opentype"}); + link.href = window.URL.createObjectURL(new Blob([font.toArrayBuffer()]), { + type: 'font/opentype', + }); link.click(); // 隱藏進度條 @@ -1060,13 +1121,60 @@ $(document).ready(async function () { $progressContainer.hide(); }); - // 顯示設定畫面 - $('#settingButton').on('click', async function () { + let justwritenowBuffer; + + const sendRightNOW = async () => { + $('#download-container').hide(); + $('#justwritenow-container').show(); + $('#justwriteNOWConfirmButton').prop('disabled', true); + $('#justwritenow-status').text(fdrawer.exportPreparingMsg); + const font = await loadWriting(); + justwritenowBuffer = font.toArrayBuffer(); + $('#justwriteNOWConfirmButton').prop('disabled', false); + $('#justwritenow-status').text(fdrawer.exportReadyMsg); + }; + + $('#justwriteNOWButton').on('click', sendRightNOW); + + $('#justwriteNOWConfirmButton').on('click', async function () { + const host = 'https://justwritenow.zeabur.app'; + const popup = window.open(`${host}/upload?send=But`, '_blank'); + if (!popup) { + alert(fdrawer.exportFailedMsg); + return; + } + + window.addEventListener('message', (event) => { + console.log('Parent received message from:', event.origin, 'data:', event.data); + if (event.origin !== host) return; + + if (event.data === 'ready') { + console.log('Received ready signal, sending buffer to popup'); + + const uint8Array = new Uint8Array(justwritenowBuffer); + const base64String = btoa(String.fromCharCode.apply(null, Array.from(uint8Array))); + + popup.postMessage( + { + bufferBase64: base64String, + byteLength: justwritenowBuffer.byteLength, + }, + event.origin + ); + } + }); + + $naviContainer.show(); + $progressContainer.hide(); + }); + + // 顯示設定畫面 + $('#settingButton').on('click', async function () { $('#settings-title').text(settings.notNewFlag ? fdrawer.settingsTitle : fdrawer.welcomeTitle); $('#span-welcome').toggle(!settings.notNewFlag); $('#div-backup').toggle(settings.notNewFlag); - $('#settings-container').show(); + $('#settings-container').show(); $('#fontNameEng').val(settings.fontNameEng); $('#fontNameCJK').val(settings.fontNameCJK); $('#smallModeCheck').prop('checked', settings.smallMode); @@ -1080,48 +1188,61 @@ $(document).ready(async function () { $('#gridTypeSelect').val(settings.gridType); if (!settings.notNewFlag) updateSetting('notNewFlag', true); // 如果是第一次使用,則設定 notNewFlag 為 true - }); + }); - // 關閉設定畫面 - $('#closeSettingsButton').on('click', function () { - $('#settings-container').hide(); - }); + // 關閉設定畫面 + $('#closeSettingsButton').on('click', function () { + $('#settings-container').hide(); + }); - $('#fontNameEng').on('change', function () { updateSetting('fontNameEng', $(this).val().replace(/[^a-zA-Z0-9 ]/g, '')); }); - $('#fontNameCJK').on('change', function () { updateSetting('fontNameCJK', $(this).val()); }); + $('#fontNameEng').on('change', function () { + updateSetting( + 'fontNameEng', + $(this) + .val() + .replace(/[^a-zA-Z0-9 ]/g, '') + ); + }); + $('#fontNameCJK').on('change', function () { + updateSetting('fontNameCJK', $(this).val()); + }); $('#smallModeCheck').on('click', async function () { await updateSetting('smallMode', $(this).prop('checked')); $('#canvas-container').toggleClass('smallmode', settings.smallMode); }); - $('#noFixedWidthFlag').on('click', function () { updateSetting('noFixedWidthFlag', $(this).prop('checked')); }); - $('#scaleRateSlider').on('input', function () { + $('#noFixedWidthFlag').on('click', function () { + updateSetting('noFixedWidthFlag', $(this).prop('checked')); + }); + $('#scaleRateSlider').on('input', function () { var rate = parseInt($(this).val(), 10); $('#scaleRateValue').text(rate + '%'); updateSetting('scaleRate', rate); initCanvas(canvas); }); - $('#pressureEffectSelect').change(function () { updateSetting('pressureEffect', $(this).val()); }); - $('#gridTypeSelect').change(function () { + $('#pressureEffectSelect').change(function () { + updateSetting('pressureEffect', $(this).val()); + }); + $('#gridTypeSelect').change(function () { updateSetting('gridType', $(this).val()); initCanvas(canvas); }); // 筆壓繪圖設定事件監聽器 - $('#pressureDrawingEnabled').on('change', async function () { + $('#pressureDrawingEnabled').on('change', async function () { updateSetting('oldPressureMode', $(this).prop('checked')); // 立即更新筆壓繪圖狀態 await updatePressureDrawingStatus(); - $('#brushSelector').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆刷選擇器 - $('#pressureButton').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆壓開關 + $('#brushSelector').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆刷選擇器 + $('#pressureButton').toggle(!settings.oldPressureMode); // 如果舊筆壓繪圖啟用,則隱藏筆壓開關 }); // 顯示字表畫面 - $('#canvasListButton').on('click', async function () { - saveToLocalDB(true); // 儲存當前畫布內容到 Local Storage + $('#canvasListButton').on('click', async function () { + saveToLocalDB(true); // 儲存當前畫布內容到 Local Storage - $('#listup-container').show(); - $('#listup-body').empty(); // 清空 + $('#listup-container').show(); + $('#listup-body').empty(); // 清空 // 計算 viewBox var scale = parseInt(settings.scaleRate, 10) / 100; @@ -1132,30 +1253,37 @@ $(document).ready(async function () { for (let i in nowList) { var gname = nowList[i]; var svgData = await loadFromDB('s_' + gname); - if (svgData) { // 已寫過 + if (svgData) { + // 已寫過 $('#listup-body').append( - $('').html('').data('index', i).on('click', function () { - setGlyph($(this).data('index')*1); - $('#listup-container').hide(); - }) + $('') + .html('') + .data('index', i) + .on('click', function () { + setGlyph($(this).data('index') * 1); + $('#listup-container').hide(); + }) ); } else { - var cell = $('').text(glyphMap[gname].c).data('index', i).on('click', function () { - setGlyph($(this).data('index')*1); - $('#listup-container').hide(); - }); + var cell = $('') + .text(glyphMap[gname].c) + .data('index', i) + .on('click', function () { + setGlyph($(this).data('index') * 1); + $('#listup-container').hide(); + }); if (glyphMap[gname].v && gname.indexOf('.vert') > 0) cell.addClass('vert'); $('#listup-body').append(cell); } } $('

').appendTo($('#listup-body')); - }); + }); - // 關閉設定畫面 - $('#closeListupButton').on('click', function () { - $('#listup-container').hide(); - }); + // 關閉設定畫面 + $('#closeListupButton').on('click', function () { + $('#listup-container').hide(); + }); // 顯示提示畫面 $('#hintButton').on('click', function () { @@ -1168,32 +1296,38 @@ $(document).ready(async function () { $('#hint-container').hide(); }); - // 顯示下載畫面 - $('#downloadButton').on('click', async function () { - $('#download-container').show(); + // 顯示下載畫面 + $('#downloadButton').on('click', async function () { + $('#download-container').show(); $('#saveAsTester').prop('checked', settings.saveAsTester); // 設定是否為測試儲存 - }); + }); - // 關閉下載畫面 - $('#closeDownloadButton').on('click', function () { - $('#download-container').hide(); - }); + // 關閉下載畫面 + $('#closeDownloadButton').on('click', function () { + $('#download-container').hide(); + }); // 關閉廣告畫面 $('#closeAdsButton').on('click', function () { $('#ads-container').hide(); }); - - - // 取得滑鼠或觸控座標 - function getCanvasCoordinates(event) { - const rect = canvas.getBoundingClientRect(); - const touch = event.type.includes('touch') ? event.originalEvent.touches[0] : event; - return { - x: touch.clientX - rect.left, - y: touch.clientY - rect.top - }; - } + + // 關閉匯出至手寫誌畫面 + $('#closeJustWriteNOWButton').on('click', function () { + $('#justwritenow-container').hide(); + $naviContainer.show(); + $progressContainer.hide(); + }); + + // 取得滑鼠或觸控座標 + function getCanvasCoordinates(event) { + const rect = canvas.getBoundingClientRect(); + const touch = event.type.includes('touch') ? event.originalEvent.touches[0] : event; + return { + x: touch.clientX - rect.left, + y: touch.clientY - rect.top, + }; + } // 匯出事件 - Debugger $('#exportEventsButton').on('click', async function () { @@ -1201,7 +1335,7 @@ $(document).ready(async function () { if (data.length > 0) { const blob = new Blob([data], { type: 'text/plain' }); const link = document.createElement('a'); - link.download = settings.fontNameEng + '-EventLog' + (new Date()).toISOString() + '.txt'; + link.download = settings.fontNameEng + '-EventLog' + new Date().toISOString() + '.txt'; link.href = window.URL.createObjectURL(blob); link.click(); } else { @@ -1216,11 +1350,11 @@ $(document).ready(async function () { const request = store.getAll(); request.onsuccess = async function (event) { - const data = event.target.result.map(item => `${item.key}\t${item.value}`).join('\n'); + const data = event.target.result.map((item) => `${item.key}\t${item.value}`).join('\n'); if (data.length > 0) { const blob = new Blob([data], { type: 'text/plain' }); const link = document.createElement('a'); - link.download = settings.fontNameEng + '-' + (new Date()).toISOString() + '.txt'; + link.download = settings.fontNameEng + '-' + new Date().toISOString() + '.txt'; link.href = window.URL.createObjectURL(blob); link.click(); } else { @@ -1229,60 +1363,64 @@ $(document).ready(async function () { }; }); - // 匯入資料 - $('#importDataFile').on('change', async function () { - if (confirm(fdrawer.importConfirm)) { - const fileInput = $(this); - const file = fileInput[0].files[0]; - if (file) { - const reader = new FileReader(); - reader.onload = async function (e) { - await clearDB(); // 清除現有的 IndexedDB 資料 - const data = e.target.result; - const lines = data.split('\n'); - for (const line of lines) { - if (line.trim() === '') continue; // 跳過空行 - const parts = line.split('\t'); - if (parts.length < 2) continue; // 如果格式不正確,跳過 - const key = parts[0].trim(); - const value = parts[1].trim(); - await saveToDB(key, value); - } - alert(fdrawer.importDone); - location.reload(); // 重新載入頁面 - }; - reader.readAsText(file); - } - } else { - $(this).val(''); // 清除選擇的檔案 - } - }); - - // 修改清除所有資料的功能 - $('#clearAllButton').on('click', async function () { - if (confirm(fdrawer.clearConfirm)) { - await clearDB(); - alert(fdrawer.clearDone); - location.reload(); // 重新載入頁面 - } - }); + // 匯入資料 + $('#importDataFile').on('change', async function () { + if (confirm(fdrawer.importConfirm)) { + const fileInput = $(this); + const file = fileInput[0].files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = async function (e) { + await clearDB(); // 清除現有的 IndexedDB 資料 + const data = e.target.result; + const lines = data.split('\n'); + for (const line of lines) { + if (line.trim() === '') continue; // 跳過空行 + const parts = line.split('\t'); + if (parts.length < 2) continue; // 如果格式不正確,跳過 + const key = parts[0].trim(); + const value = parts[1].trim(); + await saveToDB(key, value); + } + alert(fdrawer.importDone); + location.reload(); // 重新載入頁面 + }; + reader.readAsText(file); + } + } else { + $(this).val(''); // 清除選擇的檔案 + } + }); + + // 修改清除所有資料的功能 + $('#clearAllButton').on('click', async function () { + if (confirm(fdrawer.clearConfirm)) { + await clearDB(); + alert(fdrawer.clearDone); + location.reload(); // 重新載入頁面 + } + }); }); document.addEventListener('DOMContentLoaded', function () { - // 判斷是否在 in-app browser 中 - const userAgent = navigator.userAgent || navigator.vendor || window.opera; + // 判斷是否在 in-app browser 中 + const userAgent = navigator.userAgent || navigator.vendor || window.opera; - if (/FBAN|FBAV|Instagram|Line|Threads/i.test(userAgent)) { - // 如果是 Facebook、Instagram 或 Line 的 in-app browser - alert(fdrawer.inAppNotice); - } + if (/FBAN|FBAV|Instagram|Line|Threads/i.test(userAgent)) { + // 如果是 Facebook、Instagram 或 Line 的 in-app browser + alert(fdrawer.inAppNotice); + } // 解決 iOS Safari 按鈕點兩下容易不小心放大視窗的問題 - if (/iphone|ipad|ipod/.test(userAgent.toLowerCase()) && /safari/.test(userAgent.toLowerCase())) { - document.querySelectorAll('body').forEach(function(btn) { - btn.addEventListener('dblclick', function(e) { - e.preventDefault(); - }, { passive: false }); - }); - } + if (/iphone|ipad|ipod/.test(userAgent.toLowerCase()) && /safari/.test(userAgent.toLowerCase())) { + document.querySelectorAll('body').forEach(function (btn) { + btn.addEventListener( + 'dblclick', + function (e) { + e.preventDefault(); + }, + { passive: false } + ); + }); + } }); diff --git a/pages/index.html b/pages/index.html index 21aee78..f5801ed 100644 --- a/pages/index.html +++ b/pages/index.html @@ -1,177 +1,195 @@ - + - - - - 手寫字型產生器 by 字嗨 - - - -

- - - 👓 - 🔍 -
- -
- - - - 👈 - 👉 -
-
- - -
- -
-
- 🖌️🚿 - - -
- -
- 🚮 - ⬅️ - ➡️ - ⬆️ - ⬇️ - ↩️ -
- - -
- - 0% -
- -
-
- × -

字符列表

-
+ + + + 手寫字型產生器 by 字嗨 + + + +
+ + + 👓 + 🔍
-
-
-
- × -

提示 Ver

-
    -
  • 本工具由「字嗨!」提供。
  • -
  • 書寫的字表來自「jf7000當務字集」。
  • -
  • 由於所有資料儲存在您瀏覽器本地端,若擔心被瀏覽器清除,建議定期用匯出功能備份資料。
  • -
  • 建議每個字寫好後,就立刻調整位置(漢字原則上置中),這樣可避免事後調整困難。
  • -
  • 半形文字一律輸出成比例寬,故水平方向位置會被忽略。背景的紅線是基線的參考位置。
  • -
  • 全形的英數字會使用半形英數字自動產生。
  • -
  • 當所需的字符齊全時,系統會自動處理直排、組合文字的設定。
  • -
  • 請注意因為技術的限制,目前本系統所產生的OTF檔案,雖可勉強安裝運作,但並非正確的CID格式,可能在Adobe軟體無法正確識別為CJK字型。
  • -
  • 本服務所生成的字型檔,若使用上造成任何影響,本服務與作者不負任何責任。
  • -
  • 本系統並未蒐集任何使用者筆跡或其他用戶資訊。但若要公開您的字型檔時,請自行評估筆跡公開的風險。
  • -
  • 本工具不定期更新,雖然力求盡可能支援先前的版本,但要維護多種舊機制相各種環境有所困難,難以盡善盡美。由於所有版本程式碼都是開源的,可自行從GitHub上使用。若有技術支援需求,可與作者聯絡(可能須付費)。
  • -
  • 本專案GitHub頁面在這裡,在此可查閱更多資訊。

  • -
+
+ + + + 👈 + 👉 +
+
+ +
-
-
-
- × -

設定

- 在開始書寫之前,先來設定一些基本資訊吧!這些設定值之後都能再更改。
提醒您,若您是在App內的瀏覽器開啟本頁,建議用系統瀏覽器開啟,避免資料遺失!
- -

字型英文名稱

- - -

字型中文名稱

- - -

縮放率

- - 100% - 手寫時為了避免超框,容易不經意把字寫得偏小。這裡可以調整文字框的尺寸。 - -

我的字就是小

- - 適合寫字真的很小的人。 - -

背景格線樣式

- - -

中文字不等寬

- - 設定為不等寬會更像手寫字型,但可能不適合用於直排。 - -

筆壓敏感度(實驗中)

- - 本選項僅對有筆壓的設備有效。目前尚在調整中,未來修改可能無法維持舊版相容性。 - -

啟用舊筆壓模式(不建議)

- - 舊筆壓模式無法支援筆刷功能。 - -
-
-
-
-
-

完全清除字型資料

- -
-

匯出系統事件(開發用)

- -
+
+
+
+ 🖌️🚿 + + +
+ +
+ 🚮 + ⬅️ + ➡️ + ⬆️ + ⬇️ + ↩️ +
+ + +
+ + 0% +
+ +
+
+ × +

字符列表

+
-

-
-
-
- × -

下載

- -

下載字型

- - - 勾選測試輸出,字型名稱會加上流水號。避免系統快取造成在電腦上無法正常安裝使用的問題。 -
-

斗內作者

- 字型檔的權利均屬於您個人,不過如果您喜歡這個工具,請考慮斗內支持作者的開發工作。您的支持將有助於未來的更新和改進! -
-
-
-

匯入先前備份的資料

- -
-

匯出編輯中資料

- -
-

+
+
+ × +

提示 Ver

+
    +
  • 本工具由「字嗨!」提供。
  • +
  • 書寫的字表來自「jf7000當務字集」。
  • +
  • 由於所有資料儲存在您瀏覽器本地端,若擔心被瀏覽器清除,建議定期用匯出功能備份資料。
  • +
  • 建議每個字寫好後,就立刻調整位置(漢字原則上置中),這樣可避免事後調整困難。
  • +
  • 半形文字一律輸出成比例寬,故水平方向位置會被忽略。背景的紅線是基線的參考位置。
  • +
  • 全形的英數字會使用半形英數字自動產生。
  • +
  • 當所需的字符齊全時,系統會自動處理直排、組合文字的設定。
  • +
  • 請注意因為技術的限制,目前本系統所產生的OTF檔案,雖可勉強安裝運作,但並非正確的CID格式,可能在Adobe軟體無法正確識別為CJK字型。
  • +
  • 本服務所生成的字型檔,若使用上造成任何影響,本服務與作者不負任何責任。
  • +
  • 本系統並未蒐集任何使用者筆跡或其他用戶資訊。但若要公開您的字型檔時,請自行評估筆跡公開的風險。
  • +
  • + 本工具不定期更新,雖然力求盡可能支援先前的版本,但要維護多種舊機制相各種環境有所困難,難以盡善盡美。由於所有版本程式碼都是開源的,可自行從GitHub上使用。若有技術支援需求,可與作者聯絡(可能須付費)。 +
  • +
  • + 本專案GitHub頁面在這裡,在此可查閱更多資訊。 +

    +
  • +
+
+
+ +
+
+ × +

設定

+ 在開始書寫之前,先來設定一些基本資訊吧!這些設定值之後都能再更改。
提醒您,若您是在App內的瀏覽器開啟本頁,建議用系統瀏覽器開啟,避免資料遺失!
+ +

字型英文名稱

+ + +

字型中文名稱

+ + +

縮放率

+ + 100% + 手寫時為了避免超框,容易不經意把字寫得偏小。這裡可以調整文字框的尺寸。 + +

我的字就是小

+ + 適合寫字真的很小的人。 + +

背景格線樣式

+ + +

中文字不等寬

+ + 設定為不等寬會更像手寫字型,但可能不適合用於直排。 + +

筆壓敏感度(實驗中)

+ + 本選項僅對有筆壓的設備有效。目前尚在調整中,未來修改可能無法維持舊版相容性。 + +

啟用舊筆壓模式(不建議)

+ + 舊筆壓模式無法支援筆刷功能。 + +
+
+
+
+
+

完全清除字型資料

+ +
+

匯出系統事件(開發用)

+ +
+
+

+
+
+ +
+
+ × +

下載

+ +

下載字型

+ + + 勾選測試輸出,字型名稱會加上流水號。避免系統快取造成在電腦上無法正常安裝使用的問題。 +
+ +

斗內作者

+ 字型檔的權利均屬於您個人,不過如果您喜歡這個工具,請考慮斗內支持作者的開發工作。您的支持將有助於未來的更新和改進! +
+
+
+

匯入先前備份的資料

+ +
+

匯出編輯中資料

+ +
+

+
+
+ +
+
+ × +

正在傳送至手寫誌

+

處理中...

+ +
-
- - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/pages/ja.html b/pages/ja.html index fae1748..8f0a094 100644 --- a/pages/ja.html +++ b/pages/ja.html @@ -1,205 +1,225 @@ - + - - - - フォントを書こう! by 字嗨 - - - -
- - - 👓 - 🔍 -
- -
- - - - 👈 - 👉 -
-
- - -
- -
-
- 🖌️🚿 - - -
- -
- 🚮 - ⬅️ - ➡️ - ⬆️ - ⬇️ - ↩️ -
- - -
- - 0% -
- -
-
- × -

グリフ一覧

-
+ + + + フォントを書こう! by 字嗨 + + + +
+ + + 👓 + 🔍
-
- -
-
- × -

ヒント Ver

-
    -
  • このツールは「@buttaiwan」が作りました。
  • -
  • すべてのデータはブラウザに保存されるため、未完成のフォントは定期的にバックアップすることをおすすめします。
  • -
  • 文字を書いたら、直ちに位置の調整を行うことをおすすめします。一般的に仮名と漢字は中央揃え、欧文はベースライン揃えです(赤いラインは目安)。
  • -
  • 半角文字は自動的にプロポーショナル幅で出力されるため、水平方向の位置は無視されます。
  • -
  • 全角の数字やアルファベットなどは、半角のグリフを用いて自動的に生成されます。
  • -
  • 横書き用・縦書き用のグリフが両方書かれている場合、縦書きの設定は自動的に行われます。
  • -
  • ダウンロードしたフォントファイルはAirDropなどでパソコンに転送してご利用ください。
  • -
  • 生成されたフォントのあらゆる権利は、利用者に属します。公開も商用利用もご自由にどうぞ。
  • -
  • 技術的制限上、現在このツールで生成されたOTFファイルは、インストールして利用すること自体は可能ですが、正確なCIDフォーマットではないため、Adobeアプリケーションなどでは日本語フォントとして認識されない恐れがあります。
  • -
  • 本システムは、ユーザーの筆跡やその他の情報を収集することはありません。ただし、フォントファイルを公開する場合は、筆跡を公開するリスクはご自身でご判断ください。
  • -
  • 本サービスの利用によって、利用者および第三者に生じた損害について、サービス提供者は責任を負わないものとします。
  • -
  • 本サービスは不定期に更新します。できるだけ互換性をキープしますが、全てのブラウザ・デバイスをカバーすることが不可能です。技術サポートは可能だが内容によっては有料です。
  • -
  • プロジェクトGitHubページにもご参照ください。

  • -
+ +
+ + + + 👈 + 👉 +
+
+ + +
+ +
+
+
+ 🖌️🚿 + + +
+ +
+ 🚮 + ⬅️ + ➡️ + ⬆️ + ⬇️ + ↩️ +
+ + +
+ + 0% +
+ +
+
+ × +

グリフ一覧

+
+
-
- -
-
- × -

フォント設定

- まずはフォントの基本情報を設定しましょう!
アプリの組み込みブラウザを利用している場合は、データ紛失を避けるためシステムブラウザで開いてください。
- -

フォント名(英語)

- - -

フォント名(日本語)

- - -

拡大率

- - 100% - 手書きの場合、枠をはみ出さないように書くと、どうしても文字がやや小さくなりがちですが、拡大率を設定して文字枠のサイズを調整できます。 - -

小さく書きたい!

- - マジで文字が小さい人にどうぞ。 - -

背景スタイル

- - -

全角文字もプロポーショナル幅

- - 手書きフォントの性格上、固定幅よりもプロポーショナル幅の方が自然に見える場合がありますが、縦書きとしての利用に不向きです。 - -

筆圧感度(実験中)

- - このオプションは筆圧対応デバイスでのみ有効です。現在調整中であり、将来的な変更により旧バージョンとの互換性が維持されない可能性があります。 - -

旧筆圧モードの有効化(非推奨)

- - 旧筆圧モードはブラシに対応しません。 - -
-
-
-
-

フォントデータを削除する

- -
-

システムデバッグ用機能

- -
+ +
+
+ × +

ヒント Ver

+
    +
  • このツールは「@buttaiwan」が作りました。
  • +
  • すべてのデータはブラウザに保存されるため、未完成のフォントは定期的にバックアップすることをおすすめします。
  • +
  • 文字を書いたら、直ちに位置の調整を行うことをおすすめします。一般的に仮名と漢字は中央揃え、欧文はベースライン揃えです(赤いラインは目安)。
  • +
  • 半角文字は自動的にプロポーショナル幅で出力されるため、水平方向の位置は無視されます。
  • +
  • 全角の数字やアルファベットなどは、半角のグリフを用いて自動的に生成されます。
  • +
  • 横書き用・縦書き用のグリフが両方書かれている場合、縦書きの設定は自動的に行われます。
  • +
  • ダウンロードしたフォントファイルはAirDropなどでパソコンに転送してご利用ください。
  • +
  • 生成されたフォントのあらゆる権利は、利用者に属します。公開も商用利用もご自由にどうぞ。
  • +
  • + 技術的制限上、現在このツールで生成されたOTFファイルは、インストールして利用すること自体は可能ですが、正確なCIDフォーマットではないため、Adobeアプリケーションなどでは日本語フォントとして認識されない恐れがあります。 +
  • +
  • 本システムは、ユーザーの筆跡やその他の情報を収集することはありません。ただし、フォントファイルを公開する場合は、筆跡を公開するリスクはご自身でご判断ください。
  • +
  • 本サービスの利用によって、利用者および第三者に生じた損害について、サービス提供者は責任を負わないものとします。
  • +
  • 本サービスは不定期に更新します。できるだけ互換性をキープしますが、全てのブラウザ・デバイスをカバーすることが不可能です。技術サポートは可能だが内容によっては有料です。
  • +
  • + プロジェクトGitHubページにもご参照ください。 +

    +
  • +
-

-
- -
-
- × -

ダウンロード

- -

フォントのダウンロード

- - - テスト出力モードでは、フォント名に通し番号がつけられます。パソコンのフォントキャッシュによるインストールや使用の不具合を避けることができます。 -
-

寄付のお願い

- 作成したフォントのあらゆる権利はあなたに属しますが、もしこのツールが役に立ったと感じたら、ぜひ寄付をお願いします!
- PayPalはこちら/ - または台湾ドル建てはこちら -
-
-
-
-

バックアップデータの読み込み

- -
-

データをバックアップする

- -
-

+ +
+
+ × +

フォント設定

+ まずはフォントの基本情報を設定しましょう!
アプリの組み込みブラウザを利用している場合は、データ紛失を避けるためシステムブラウザで開いてください。
+ +

フォント名(英語)

+ + +

フォント名(日本語)

+ + +

拡大率

+ + 100% + 手書きの場合、枠をはみ出さないように書くと、どうしても文字がやや小さくなりがちですが、拡大率を設定して文字枠のサイズを調整できます。 + +

小さく書きたい!

+ + マジで文字が小さい人にどうぞ。 + +

背景スタイル

+ + +

全角文字もプロポーショナル幅

+ + 手書きフォントの性格上、固定幅よりもプロポーショナル幅の方が自然に見える場合がありますが、縦書きとしての利用に不向きです。 + +

筆圧感度(実験中)

+ + このオプションは筆圧対応デバイスでのみ有効です。現在調整中であり、将来的な変更により旧バージョンとの互換性が維持されない可能性があります。 + +

旧筆圧モードの有効化(非推奨)

+ + 旧筆圧モードはブラシに対応しません。 + +
+
+
+
+

フォントデータを削除する

+ +
+

システムデバッグ用機能

+ +
+
+

+
-
- - - - - - - - - - - \ No newline at end of file + +
+
+ × +

ダウンロード

+ +

フォントのダウンロード

+ + + テスト出力モードでは、フォント名に通し番号がつけられます。パソコンのフォントキャッシュによるインストールや使用の不具合を避けることができます。 +
+ +

寄付のお願い

+ 作成したフォントのあらゆる権利はあなたに属しますが、もしこのツールが役に立ったと感じたら、ぜひ寄付をお願いします!
+ PayPalはこちら/ または台湾ドル建てはこちら +
+
+
+
+

バックアップデータの読み込み

+ +
+

データをバックアップする

+ +
+

+
+
+ +
+
+ × +

手書き誌へ送信中

+

処理中...

+ +
+
+ + + + + + + + + + + diff --git a/pages/pressure-drawing.js b/pages/pressure-drawing.js index db29fd6..54a3033 100644 --- a/pages/pressure-drawing.js +++ b/pages/pressure-drawing.js @@ -4,570 +4,572 @@ */ class PressureDrawing { - constructor() { - this.perfectFreehandModule = null; - this.currentStroke = []; - this.isDrawing = false; - this.lastPoint = null; - this.hasPressureSupport = false; // 檢測是否支援筆壓 - this.pressureCheckCount = 0; // 用於檢測筆壓支援 - this.delayedStart = false; // 是否在延遲繪製狀態 - this.startPoint = null; // 起筆點 - this.moveThreshold = 5; // 移動閾值(像素) - } - - // Initialize the perfect-freehand module - async initialize() { - try { - this.perfectFreehandModule = await import('https://unpkg.com/perfect-freehand@1.2.2/dist/esm/index.mjs'); - return true; - } catch (error) { - return false; - } - } - - // Start a new stroke - startStroke(x, y, pressure = 0.5) { - this.isDrawing = true; - this.currentStroke = []; - this.lastPoint = { x, y, pressure }; - this.delayedStart = true; // 進入延遲繪製狀態 - this.startPoint = { x, y, pressure }; - this.currentStroke.push([x, y, pressure]); - } - - // Add a point to the current stroke - addPoint(x, y, pressure = 0.5) { - if (!this.isDrawing) return; - - // 檢查是否在延遲繪製狀態 - if (this.delayedStart && this.startPoint) { - const dx = x - this.startPoint.x; - const dy = y - this.startPoint.y; - const distance = Math.sqrt(dx * dx + dy * dy); - - if (distance < this.moveThreshold) { - // 還沒有足夠的移動,只更新起始點的壓力 - this.startPoint.pressure = Math.max(this.startPoint.pressure, pressure); - this.currentStroke[0] = [this.startPoint.x, this.startPoint.y, this.startPoint.pressure]; - this.lastPoint = { x, y, pressure }; - return; - } else { - // 開始真正的繪製 - this.delayedStart = false; - } - } - - let isSimulatedPressure = false; - - // 如果沒有真實壓力支持且檢測計數足夠,才進行速度模擬 - if (pressure === 0.5 && this.lastPoint && !this.hasPressureSupport && this.pressureCheckCount > 3) { - const dx = x - this.lastPoint.x; - const dy = y - this.lastPoint.y; - const distance = Math.sqrt(dx * dx + dy * dy); - - // Adjust pressure based on drawing speed (slower = more pressure) - const speedFactor = Math.min(1, 10 / Math.max(distance, 1)); - pressure = 0.4 + speedFactor * 0.4; // Range from 0.4 to 0.8,範圍較小避免極端值 - isSimulatedPressure = true; - } - - // 只對模擬的壓力值增強對比,讓效果更明顯 - if (isSimulatedPressure) { - if (pressure < 0.5) { - pressure = pressure * 0.9; // 低壓力稍微降低 - } else { - pressure = 0.4 + (pressure - 0.5) * 1.1; // 高壓力稍微增加 - } - } - - this.currentStroke.push([x, y, pressure]); - this.lastPoint = { x, y, pressure }; - } - - // Finish the current stroke and return the path - finishStroke(options = {}) { - if (!this.isDrawing) { - this.delayedStart = false; - this.startPoint = null; - return null; - } - - // 如果還在延遲繪製狀態,說明沒有足夠的移動,直接生成圓形點 - if (this.delayedStart && this.startPoint) { - this.isDrawing = false; - this.delayedStart = false; - const strokePoints = [[this.startPoint.x, this.startPoint.y, this.startPoint.pressure]]; - this.startPoint = null; - return this.generateCircularDot(strokePoints, options); - } - - // 如果筆跡太短(只有起始點),也生成圓形點 - if (this.currentStroke.length < 2) { - this.isDrawing = false; - this.delayedStart = false; - const strokePoints = [...this.currentStroke]; - this.startPoint = null; - return this.generateCircularDot(strokePoints, options); - } - - // 動態決定是否模擬壓力 - const shouldSimulatePressure = !this.hasPressureSupport && this.pressureCheckCount > 3; - - const defaultOptions = { - size: 12, - thinning: 0.8, // 增加壓力對粗細的影響 - smoothing: 0.5, - streamline: 0.3, // 減少流線化,讓壓力變化更明顯 - simulatePressure: shouldSimulatePressure, // 動態決定是否模擬壓力 - easing: (t) => t, - start: { - taper: 0, - easing: (t) => t, - cap: true - }, - end: { - taper: 25, // 大幅增加結尾的漸減效果 - easing: (t) => Math.sin((t * Math.PI) / 2), // 使用正弦緩動,更平滑 - cap: true // 使用圓頭來避免尖銳結尾 - } - }; - - const finalOptions = { ...defaultOptions, ...options }; - - try { - // 複製 stroke 點,避免修改原始資料 - let strokePoints = [...this.currentStroke]; - - // 壓力平滑處理 - if (strokePoints.length > 5) { - strokePoints = this.smoothPressureValues(strokePoints); - } - - - - // 檢查是否為靜止點或極短筆跡 - if (strokePoints.length <= 3) { - this.isDrawing = false; - this.currentStroke = []; - this.delayedStart = false; - this.startPoint = null; - return this.generateCircularDot(strokePoints, finalOptions); - } - - // 檢查筆跡是否在很小的範圍內 - const bounds = this.calculateBounds(strokePoints); - const maxDistance = Math.max(bounds.width, bounds.height); - - if (maxDistance < 8) { // 如果筆跡範圍小於 8 像素 - this.isDrawing = false; - this.currentStroke = []; - this.delayedStart = false; - this.startPoint = null; - return this.generateCircularDot(strokePoints, finalOptions); - } - - // Get stroke outline from perfect-freehand - const outlinePoints = this.perfectFreehandModule.getStroke(strokePoints, finalOptions); - - this.isDrawing = false; - this.currentStroke = []; - this.delayedStart = false; - this.startPoint = null; - - return outlinePoints; - } catch (error) { - this.isDrawing = false; - this.currentStroke = []; - this.delayedStart = false; - this.startPoint = null; - return null; - } - } - - // Smooth pressure values to create natural light-heavy-light curve - smoothPressureValues(strokePoints) { - if (!strokePoints || strokePoints.length < 5) return strokePoints; - - const points = [...strokePoints]; - const len = points.length; - - // Step 1: 移除開始和結尾的不穩定區域 - let startIndex = 0; - let endIndex = len; - - // 找到壓力開始穩定的位置 - for (let i = 2; i < Math.min(len - 2, 15); i++) { - const pressureVariance = this.calculatePressureVariance(points, i - 2, i + 2); - if (pressureVariance < 0.15) { // 放寬穩定標準 - startIndex = i; - break; - } - } - - // 找到壓力結束穩定的位置 - for (let i = len - 3; i >= Math.max(startIndex + 2, len - 15); i--) { - const pressureVariance = this.calculatePressureVariance(points, i - 2, i + 2); - if (pressureVariance < 0.15) { // 放寬穩定標準 - endIndex = i + 1; - break; - } - } - - // Step 2: 截取穩定區域 - const stablePoints = points.slice(startIndex, endIndex); - - if (stablePoints.length < 3) return points; // 如果穩定區域太小,返回原始數據 - - // Step 3: 對穩定區域進行移動平均平滑 - const smoothedPoints = this.applyMovingAverage(stablePoints); - - // Step 4: 創建自然的起筆和收筆 - const naturalCurve = this.createNaturalPressureCurve(smoothedPoints); - - return naturalCurve; - } - - // Calculate pressure variance in a range - calculatePressureVariance(points, start, end) { - if (start < 0 || end >= points.length || end - start < 2) return 999; - - const pressures = points.slice(start, end + 1).map(p => p[2]); - const mean = pressures.reduce((a, b) => a + b) / pressures.length; - const variance = pressures.reduce((acc, p) => acc + Math.pow(p - mean, 2), 0) / pressures.length; - - return Math.sqrt(variance); - } - - // Apply moving average to smooth pressure values - applyMovingAverage(points) { - if (points.length < 3) return points; - - const smoothed = []; - const windowSize = 5; // 增加窗口大小,讓平滑效果更明顯 - - for (let i = 0; i < points.length; i++) { - const start = Math.max(0, i - Math.floor(windowSize / 2)); - const end = Math.min(points.length - 1, i + Math.floor(windowSize / 2)); - - let sumPressure = 0; - let count = 0; - - for (let j = start; j <= end; j++) { - sumPressure += points[j][2]; - count++; - } - - const avgPressure = sumPressure / count; - smoothed.push([points[i][0], points[i][1], avgPressure]); - } - - return smoothed; - } - - // Create natural pressure curve with light start and end - createNaturalPressureCurve(points) { - if (points.length < 3) return points; - - const result = [...points]; - const len = result.length; - - // 找到壓力的峰值位置 - let maxPressure = 0; - let maxIndex = Math.floor(len / 2); - - for (let i = 0; i < len; i++) { - if (result[i][2] > maxPressure) { - maxPressure = result[i][2]; - maxIndex = i; - } - } - - // 創建自然的壓力曲線:輕 -> 重 -> 輕 - for (let i = 0; i < len; i++) { - let factor = 1.0; - - if (i < maxIndex) { - // 起筆段:從 0.3 漸增到 1.0 - factor = 0.3 + 0.7 * (i / maxIndex); - } else { - // 收筆段:從 1.0 漸減到 0.2 - factor = 1.0 - 0.8 * ((i - maxIndex) / (len - 1 - maxIndex)); - factor = Math.max(0.2, factor); - } - - // 應用漸變係數,但保持原始壓力的相對變化 - result[i][2] = result[i][2] * factor; - } - - return result; - } - - // Calculate bounds of stroke points - calculateBounds(strokePoints) { - if (!strokePoints || strokePoints.length === 0) { - return { width: 0, height: 0, minX: 0, maxX: 0, minY: 0, maxY: 0 }; - } - - let minX = strokePoints[0][0]; - let maxX = strokePoints[0][0]; - let minY = strokePoints[0][1]; - let maxY = strokePoints[0][1]; - - for (let i = 1; i < strokePoints.length; i++) { - const [x, y] = strokePoints[i]; - minX = Math.min(minX, x); - maxX = Math.max(maxX, x); - minY = Math.min(minY, y); - maxY = Math.max(maxY, y); - } - - return { - width: maxX - minX, - height: maxY - minY, - minX, maxX, minY, maxY - }; - } - - // Generate circular dot based on pressure - generateCircularDot(strokePoints, options) { - if (!strokePoints || strokePoints.length === 0) return []; - - // 計算中心點和平均壓力 - let centerX = 0; - let centerY = 0; - let totalPressure = 0; - - for (const point of strokePoints) { - centerX += point[0]; - centerY += point[1]; - totalPressure += point[2]; - } - - centerX /= strokePoints.length; - centerY /= strokePoints.length; - const avgPressure = totalPressure / strokePoints.length; - - // 根據壓力計算半徑 - const baseRadius = (options.size || 12) * 0.6; // 增加基礎半徑 - const radius = baseRadius * (0.4 + avgPressure * 0.8); // 增加最小和最大半徑 - - // 生成圓形的點 - const circlePoints = []; - const segments = 16; // 圓形分段數 - - for (let i = 0; i < segments; i++) { - const angle = (i * 2 * Math.PI) / segments; - const x = centerX + Math.cos(angle) * radius; - const y = centerY + Math.sin(angle) * radius; - circlePoints.push([x, y]); - } - - return circlePoints; - } - - // Convert stroke outline to SVG path - outlineToSVGPath(outlinePoints) { - if (!outlinePoints || outlinePoints.length < 2) return ''; - - const path = outlinePoints.reduce((acc, point, index) => { - const [x, y] = point; - if (index === 0) { - return `M${x},${y}`; - } - return `${acc}L${x},${y}`; - }, ''); - - return `${path}Z`; - } - - // Draw stroke outline on canvas - drawStrokeOnCanvas(ctx, outlinePoints, eraseMode = false) { - if (!outlinePoints || outlinePoints.length < 2) return; - - ctx.save(); - - // Set composite operation for erasing - ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; - - // Create path from outline points - ctx.beginPath(); - outlinePoints.forEach((point, index) => { - const [x, y] = point; - if (index === 0) { - ctx.moveTo(x, y); - } else { - ctx.lineTo(x, y); - } - }); - ctx.closePath(); - - // Fill the path - ctx.fillStyle = eraseMode ? 'rgba(0,0,0,1)' : 'black'; - ctx.fill(); - - ctx.restore(); - } - - // Get current stroke points (for preview) - getCurrentStrokePoints() { - return [...this.currentStroke]; - } - - // Check if currently drawing - getIsDrawing() { - return this.isDrawing; - } - - // Cancel current stroke - cancelStroke() { - this.isDrawing = false; - this.currentStroke = []; - this.lastPoint = null; - this.delayedStart = false; - this.startPoint = null; - } - - // Reset pressure detection (useful when switching characters) - resetPressureDetection() { - this.hasPressureSupport = false; - this.pressureCheckCount = 0; - this.delayedStart = false; - this.startPoint = null; - } - - // Create a preview stroke (for real-time drawing feedback) - createPreviewStroke(options = {}) { - if (!this.isDrawing || this.currentStroke.length < 8) return null; - - // 在延遲繪製狀態下不生成預覽筆跡 - if (this.delayedStart) return null; - - // 動態決定是否模擬壓力 - const shouldSimulatePressure = !this.hasPressureSupport && this.pressureCheckCount > 3; - - const defaultOptions = { - size: 12, - thinning: 0.8, // 增加壓力對粗細的影響 - smoothing: 0.5, - streamline: 0.3, // 減少流線化,讓壓力變化更明顯 - simulatePressure: shouldSimulatePressure, // 動態決定是否模擬壓力 - easing: (t) => t, - start: { - taper: 0, - easing: (t) => t, - cap: true - }, - end: { - taper: 25, // 大幅增加結尾的漸減效果 - easing: (t) => Math.sin((t * Math.PI) / 2), // 使用正弦緩動,更平滑 - cap: true // 使用圓頭來避免尖銳結尾 - } - }; - - const finalOptions = { ...defaultOptions, ...options }; - - try { - // 對預覽筆跡也應用壓力平滑 - let previewPoints = [...this.currentStroke]; - if (previewPoints.length > 5) { - previewPoints = this.smoothPressureValues(previewPoints); - } - - return this.perfectFreehandModule.getStroke(previewPoints, finalOptions); - } catch (error) { - return null; - } - } - - // Simulate pressure from pointer events - simulatePressure(event, eventType = 'move') { - - - // 提筆事件特殊處理 - 使用較低的壓力值 - if (eventType === 'end' && this.lastPoint) { - return Math.max(0.05, this.lastPoint.pressure * 0.3); // 提筆時壓力大幅減少 - } - - // Try to get pressure from pointer event (works with Apple Pencil) - if (event && event.pressure !== undefined && event.pressure > 0.1 && event.pointerType === 'pen') { - // 只有筆類型的 pointer event 且壓力值 > 0.1 才算真實筆壓支援 - this.hasPressureSupport = true; - - // 提筆事件時限制最大壓力值 - let pressure = event.pressure; - if (eventType === 'end') { - pressure = Math.min(pressure, 0.6); // 提筆時限制最大壓力 - } - - return Math.max(0.1, Math.min(1.0, pressure)); - } - - // 非筆類型的 pointer events(如手指)使用模擬壓力 - if (event && event.pointerType && event.pointerType !== 'pen') { - this.pressureCheckCount++; - return 0.5; // 返回預設值,讓速度模擬邏輯處理 - } - - // Try to get pressure from touch event - if (event && event.touches && event.touches.length > 0) { - const touch = event.touches[0]; - - // Apple Pencil support through force property - if (touch.force !== undefined && touch.force > 0.1 && touch.touchType === 'stylus') { - // 只有觸控筆類型且 force > 0.1 才算真實筆壓支援 - this.hasPressureSupport = true; - - // 提筆事件時限制最大壓力值 - let force = touch.force; - if (eventType === 'end') { - force = Math.min(force, 0.6); // 提筆時限制最大壓力 - } - - return Math.max(0.1, Math.min(1.0, force)); - } - - // 其他觸控事件(手指觸控或沒有 touchType)不提供真實壓力,使用模擬壓力 - if (!touch.touchType || touch.touchType !== 'stylus') { - this.pressureCheckCount++; - return 0.5; // 返回預設值,讓速度模擬邏輯處理 - } - } - - // Try to get pressure from mouse/pointer events - if (event && event.buttons !== undefined && event.type && event.type.includes('mouse')) { - // For mouse events, don't claim pressure support and use default simulation - // 滑鼠事件不提供真實壓力,應該使用模擬壓力 - this.pressureCheckCount++; - return 0.5; // 返回預設值,讓 addPoint 中的速度模擬邏輯處理 - } - - // Check for webkitForce (Safari specific for Force Touch) - if (event && event.webkitForce !== undefined && event.webkitForce > 1.0) { - // 只有 webkitForce > 1.0 才算真實壓力支援(正常值為 1.0,有壓力時會超過) - this.hasPressureSupport = true; - - // 提筆事件時限制最大壓力值 - let force = event.webkitForce; - if (eventType === 'end') { - force = Math.min(force, 2.0); // 提筆時限制最大壓力 - } - - // webkitForce 的範圍通常是 1.0-3.0,需要映射到 0.1-1.0 - const normalizedForce = Math.max(0.1, Math.min(1.0, (force - 1.0) / 2.0 + 0.5)); - return normalizedForce; - } - - // 增加檢測計數 - this.pressureCheckCount++; - - // Default pressure simulation with slight randomization - if (eventType === 'end') { - return 0.3; // 提筆時使用固定的低壓力值 - } - return 0.5 + Math.random() * 0.3; // Random pressure between 0.5 and 0.8 - } + constructor() { + this.perfectFreehandModule = null; + this.currentStroke = []; + this.isDrawing = false; + this.lastPoint = null; + this.hasPressureSupport = false; // 檢測是否支援筆壓 + this.pressureCheckCount = 0; // 用於檢測筆壓支援 + this.delayedStart = false; // 是否在延遲繪製狀態 + this.startPoint = null; // 起筆點 + this.moveThreshold = 5; // 移動閾值(像素) + } + + // Initialize the perfect-freehand module + async initialize() { + try { + this.perfectFreehandModule = await import('https://unpkg.com/perfect-freehand@1.2.2/dist/esm/index.mjs'); + return true; + } catch (error) { + return false; + } + } + + // Start a new stroke + startStroke(x, y, pressure = 0.5) { + this.isDrawing = true; + this.currentStroke = []; + this.lastPoint = { x, y, pressure }; + this.delayedStart = true; // 進入延遲繪製狀態 + this.startPoint = { x, y, pressure }; + this.currentStroke.push([x, y, pressure]); + } + + // Add a point to the current stroke + addPoint(x, y, pressure = 0.5) { + if (!this.isDrawing) return; + + // 檢查是否在延遲繪製狀態 + if (this.delayedStart && this.startPoint) { + const dx = x - this.startPoint.x; + const dy = y - this.startPoint.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < this.moveThreshold) { + // 還沒有足夠的移動,只更新起始點的壓力 + this.startPoint.pressure = Math.max(this.startPoint.pressure, pressure); + this.currentStroke[0] = [this.startPoint.x, this.startPoint.y, this.startPoint.pressure]; + this.lastPoint = { x, y, pressure }; + return; + } else { + // 開始真正的繪製 + this.delayedStart = false; + } + } + + let isSimulatedPressure = false; + + // 如果沒有真實壓力支持且檢測計數足夠,才進行速度模擬 + if (pressure === 0.5 && this.lastPoint && !this.hasPressureSupport && this.pressureCheckCount > 3) { + const dx = x - this.lastPoint.x; + const dy = y - this.lastPoint.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + // Adjust pressure based on drawing speed (slower = more pressure) + const speedFactor = Math.min(1, 10 / Math.max(distance, 1)); + pressure = 0.4 + speedFactor * 0.4; // Range from 0.4 to 0.8,範圍較小避免極端值 + isSimulatedPressure = true; + } + + // 只對模擬的壓力值增強對比,讓效果更明顯 + if (isSimulatedPressure) { + if (pressure < 0.5) { + pressure = pressure * 0.9; // 低壓力稍微降低 + } else { + pressure = 0.4 + (pressure - 0.5) * 1.1; // 高壓力稍微增加 + } + } + + this.currentStroke.push([x, y, pressure]); + this.lastPoint = { x, y, pressure }; + } + + // Finish the current stroke and return the path + finishStroke(options = {}) { + if (!this.isDrawing) { + this.delayedStart = false; + this.startPoint = null; + return null; + } + + // 如果還在延遲繪製狀態,說明沒有足夠的移動,直接生成圓形點 + if (this.delayedStart && this.startPoint) { + this.isDrawing = false; + this.delayedStart = false; + const strokePoints = [[this.startPoint.x, this.startPoint.y, this.startPoint.pressure]]; + this.startPoint = null; + return this.generateCircularDot(strokePoints, options); + } + + // 如果筆跡太短(只有起始點),也生成圓形點 + if (this.currentStroke.length < 2) { + this.isDrawing = false; + this.delayedStart = false; + const strokePoints = [...this.currentStroke]; + this.startPoint = null; + return this.generateCircularDot(strokePoints, options); + } + + // 動態決定是否模擬壓力 + const shouldSimulatePressure = !this.hasPressureSupport && this.pressureCheckCount > 3; + + const defaultOptions = { + size: 12, + thinning: 0.8, // 增加壓力對粗細的影響 + smoothing: 0.5, + streamline: 0.3, // 減少流線化,讓壓力變化更明顯 + simulatePressure: shouldSimulatePressure, // 動態決定是否模擬壓力 + easing: (t) => t, + start: { + taper: 0, + easing: (t) => t, + cap: true, + }, + end: { + taper: 25, // 大幅增加結尾的漸減效果 + easing: (t) => Math.sin((t * Math.PI) / 2), // 使用正弦緩動,更平滑 + cap: true, // 使用圓頭來避免尖銳結尾 + }, + }; + + const finalOptions = { ...defaultOptions, ...options }; + + try { + // 複製 stroke 點,避免修改原始資料 + let strokePoints = [...this.currentStroke]; + + // 壓力平滑處理 + if (strokePoints.length > 5) { + strokePoints = this.smoothPressureValues(strokePoints); + } + + // 檢查是否為靜止點或極短筆跡 + if (strokePoints.length <= 3) { + this.isDrawing = false; + this.currentStroke = []; + this.delayedStart = false; + this.startPoint = null; + return this.generateCircularDot(strokePoints, finalOptions); + } + + // 檢查筆跡是否在很小的範圍內 + const bounds = this.calculateBounds(strokePoints); + const maxDistance = Math.max(bounds.width, bounds.height); + + if (maxDistance < 8) { + // 如果筆跡範圍小於 8 像素 + this.isDrawing = false; + this.currentStroke = []; + this.delayedStart = false; + this.startPoint = null; + return this.generateCircularDot(strokePoints, finalOptions); + } + + // Get stroke outline from perfect-freehand + const outlinePoints = this.perfectFreehandModule.getStroke(strokePoints, finalOptions); + + this.isDrawing = false; + this.currentStroke = []; + this.delayedStart = false; + this.startPoint = null; + + return outlinePoints; + } catch (error) { + this.isDrawing = false; + this.currentStroke = []; + this.delayedStart = false; + this.startPoint = null; + return null; + } + } + + // Smooth pressure values to create natural light-heavy-light curve + smoothPressureValues(strokePoints) { + if (!strokePoints || strokePoints.length < 5) return strokePoints; + + const points = [...strokePoints]; + const len = points.length; + + // Step 1: 移除開始和結尾的不穩定區域 + let startIndex = 0; + let endIndex = len; + + // 找到壓力開始穩定的位置 + for (let i = 2; i < Math.min(len - 2, 15); i++) { + const pressureVariance = this.calculatePressureVariance(points, i - 2, i + 2); + if (pressureVariance < 0.15) { + // 放寬穩定標準 + startIndex = i; + break; + } + } + + // 找到壓力結束穩定的位置 + for (let i = len - 3; i >= Math.max(startIndex + 2, len - 15); i--) { + const pressureVariance = this.calculatePressureVariance(points, i - 2, i + 2); + if (pressureVariance < 0.15) { + // 放寬穩定標準 + endIndex = i + 1; + break; + } + } + + // Step 2: 截取穩定區域 + const stablePoints = points.slice(startIndex, endIndex); + + if (stablePoints.length < 3) return points; // 如果穩定區域太小,返回原始數據 + + // Step 3: 對穩定區域進行移動平均平滑 + const smoothedPoints = this.applyMovingAverage(stablePoints); + + // Step 4: 創建自然的起筆和收筆 + const naturalCurve = this.createNaturalPressureCurve(smoothedPoints); + + return naturalCurve; + } + + // Calculate pressure variance in a range + calculatePressureVariance(points, start, end) { + if (start < 0 || end >= points.length || end - start < 2) return 999; + + const pressures = points.slice(start, end + 1).map((p) => p[2]); + const mean = pressures.reduce((a, b) => a + b) / pressures.length; + const variance = pressures.reduce((acc, p) => acc + Math.pow(p - mean, 2), 0) / pressures.length; + + return Math.sqrt(variance); + } + + // Apply moving average to smooth pressure values + applyMovingAverage(points) { + if (points.length < 3) return points; + + const smoothed = []; + const windowSize = 5; // 增加窗口大小,讓平滑效果更明顯 + + for (let i = 0; i < points.length; i++) { + const start = Math.max(0, i - Math.floor(windowSize / 2)); + const end = Math.min(points.length - 1, i + Math.floor(windowSize / 2)); + + let sumPressure = 0; + let count = 0; + + for (let j = start; j <= end; j++) { + sumPressure += points[j][2]; + count++; + } + + const avgPressure = sumPressure / count; + smoothed.push([points[i][0], points[i][1], avgPressure]); + } + + return smoothed; + } + + // Create natural pressure curve with light start and end + createNaturalPressureCurve(points) { + if (points.length < 3) return points; + + const result = [...points]; + const len = result.length; + + // 找到壓力的峰值位置 + let maxPressure = 0; + let maxIndex = Math.floor(len / 2); + + for (let i = 0; i < len; i++) { + if (result[i][2] > maxPressure) { + maxPressure = result[i][2]; + maxIndex = i; + } + } + + // 創建自然的壓力曲線:輕 -> 重 -> 輕 + for (let i = 0; i < len; i++) { + let factor = 1.0; + + if (i < maxIndex) { + // 起筆段:從 0.3 漸增到 1.0 + factor = 0.3 + 0.7 * (i / maxIndex); + } else { + // 收筆段:從 1.0 漸減到 0.2 + factor = 1.0 - 0.8 * ((i - maxIndex) / (len - 1 - maxIndex)); + factor = Math.max(0.2, factor); + } + + // 應用漸變係數,但保持原始壓力的相對變化 + result[i][2] = result[i][2] * factor; + } + + return result; + } + + // Calculate bounds of stroke points + calculateBounds(strokePoints) { + if (!strokePoints || strokePoints.length === 0) { + return { width: 0, height: 0, minX: 0, maxX: 0, minY: 0, maxY: 0 }; + } + + let minX = strokePoints[0][0]; + let maxX = strokePoints[0][0]; + let minY = strokePoints[0][1]; + let maxY = strokePoints[0][1]; + + for (let i = 1; i < strokePoints.length; i++) { + const [x, y] = strokePoints[i]; + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y); + } + + return { + width: maxX - minX, + height: maxY - minY, + minX, + maxX, + minY, + maxY, + }; + } + + // Generate circular dot based on pressure + generateCircularDot(strokePoints, options) { + if (!strokePoints || strokePoints.length === 0) return []; + + // 計算中心點和平均壓力 + let centerX = 0; + let centerY = 0; + let totalPressure = 0; + + for (const point of strokePoints) { + centerX += point[0]; + centerY += point[1]; + totalPressure += point[2]; + } + + centerX /= strokePoints.length; + centerY /= strokePoints.length; + const avgPressure = totalPressure / strokePoints.length; + + // 根據壓力計算半徑 + const baseRadius = (options.size || 12) * 0.6; // 增加基礎半徑 + const radius = baseRadius * (0.4 + avgPressure * 0.8); // 增加最小和最大半徑 + + // 生成圓形的點 + const circlePoints = []; + const segments = 16; // 圓形分段數 + + for (let i = 0; i < segments; i++) { + const angle = (i * 2 * Math.PI) / segments; + const x = centerX + Math.cos(angle) * radius; + const y = centerY + Math.sin(angle) * radius; + circlePoints.push([x, y]); + } + + return circlePoints; + } + + // Convert stroke outline to SVG path + outlineToSVGPath(outlinePoints) { + if (!outlinePoints || outlinePoints.length < 2) return ''; + + const path = outlinePoints.reduce((acc, point, index) => { + const [x, y] = point; + if (index === 0) { + return `M${x},${y}`; + } + return `${acc}L${x},${y}`; + }, ''); + + return `${path}Z`; + } + + // Draw stroke outline on canvas + drawStrokeOnCanvas(ctx, outlinePoints, eraseMode = false) { + if (!outlinePoints || outlinePoints.length < 2) return; + + ctx.save(); + + // Set composite operation for erasing + ctx.globalCompositeOperation = eraseMode ? 'destination-out' : 'source-over'; + + // Create path from outline points + ctx.beginPath(); + outlinePoints.forEach((point, index) => { + const [x, y] = point; + if (index === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + }); + ctx.closePath(); + + // Fill the path + ctx.fillStyle = eraseMode ? 'rgba(0,0,0,1)' : 'black'; + ctx.fill(); + + ctx.restore(); + } + + // Get current stroke points (for preview) + getCurrentStrokePoints() { + return [...this.currentStroke]; + } + + // Check if currently drawing + getIsDrawing() { + return this.isDrawing; + } + + // Cancel current stroke + cancelStroke() { + this.isDrawing = false; + this.currentStroke = []; + this.lastPoint = null; + this.delayedStart = false; + this.startPoint = null; + } + + // Reset pressure detection (useful when switching characters) + resetPressureDetection() { + this.hasPressureSupport = false; + this.pressureCheckCount = 0; + this.delayedStart = false; + this.startPoint = null; + } + + // Create a preview stroke (for real-time drawing feedback) + createPreviewStroke(options = {}) { + if (!this.isDrawing || this.currentStroke.length < 8) return null; + + // 在延遲繪製狀態下不生成預覽筆跡 + if (this.delayedStart) return null; + + // 動態決定是否模擬壓力 + const shouldSimulatePressure = !this.hasPressureSupport && this.pressureCheckCount > 3; + + const defaultOptions = { + size: 12, + thinning: 0.8, // 增加壓力對粗細的影響 + smoothing: 0.5, + streamline: 0.3, // 減少流線化,讓壓力變化更明顯 + simulatePressure: shouldSimulatePressure, // 動態決定是否模擬壓力 + easing: (t) => t, + start: { + taper: 0, + easing: (t) => t, + cap: true, + }, + end: { + taper: 25, // 大幅增加結尾的漸減效果 + easing: (t) => Math.sin((t * Math.PI) / 2), // 使用正弦緩動,更平滑 + cap: true, // 使用圓頭來避免尖銳結尾 + }, + }; + + const finalOptions = { ...defaultOptions, ...options }; + + try { + // 對預覽筆跡也應用壓力平滑 + let previewPoints = [...this.currentStroke]; + if (previewPoints.length > 5) { + previewPoints = this.smoothPressureValues(previewPoints); + } + + return this.perfectFreehandModule.getStroke(previewPoints, finalOptions); + } catch (error) { + return null; + } + } + + // Simulate pressure from pointer events + simulatePressure(event, eventType = 'move') { + // 提筆事件特殊處理 - 使用較低的壓力值 + if (eventType === 'end' && this.lastPoint) { + return Math.max(0.05, this.lastPoint.pressure * 0.3); // 提筆時壓力大幅減少 + } + + // Try to get pressure from pointer event (works with Apple Pencil) + if (event && event.pressure !== undefined && event.pressure > 0.1 && event.pointerType === 'pen') { + // 只有筆類型的 pointer event 且壓力值 > 0.1 才算真實筆壓支援 + this.hasPressureSupport = true; + + // 提筆事件時限制最大壓力值 + let pressure = event.pressure; + if (eventType === 'end') { + pressure = Math.min(pressure, 0.6); // 提筆時限制最大壓力 + } + + return Math.max(0.1, Math.min(1.0, pressure)); + } + + // 非筆類型的 pointer events(如手指)使用模擬壓力 + if (event && event.pointerType && event.pointerType !== 'pen') { + this.pressureCheckCount++; + return 0.5; // 返回預設值,讓速度模擬邏輯處理 + } + + // Try to get pressure from touch event + if (event && event.touches && event.touches.length > 0) { + const touch = event.touches[0]; + + // Apple Pencil support through force property + if (touch.force !== undefined && touch.force > 0.1 && touch.touchType === 'stylus') { + // 只有觸控筆類型且 force > 0.1 才算真實筆壓支援 + this.hasPressureSupport = true; + + // 提筆事件時限制最大壓力值 + let force = touch.force; + if (eventType === 'end') { + force = Math.min(force, 0.6); // 提筆時限制最大壓力 + } + + return Math.max(0.1, Math.min(1.0, force)); + } + + // 其他觸控事件(手指觸控或沒有 touchType)不提供真實壓力,使用模擬壓力 + if (!touch.touchType || touch.touchType !== 'stylus') { + this.pressureCheckCount++; + return 0.5; // 返回預設值,讓速度模擬邏輯處理 + } + } + + // Try to get pressure from mouse/pointer events + if (event && event.buttons !== undefined && event.type && event.type.includes('mouse')) { + // For mouse events, don't claim pressure support and use default simulation + // 滑鼠事件不提供真實壓力,應該使用模擬壓力 + this.pressureCheckCount++; + return 0.5; // 返回預設值,讓 addPoint 中的速度模擬邏輯處理 + } + + // Check for webkitForce (Safari specific for Force Touch) + if (event && event.webkitForce !== undefined && event.webkitForce > 1.0) { + // 只有 webkitForce > 1.0 才算真實壓力支援(正常值為 1.0,有壓力時會超過) + this.hasPressureSupport = true; + + // 提筆事件時限制最大壓力值 + let force = event.webkitForce; + if (eventType === 'end') { + force = Math.min(force, 2.0); // 提筆時限制最大壓力 + } + + // webkitForce 的範圍通常是 1.0-3.0,需要映射到 0.1-1.0 + const normalizedForce = Math.max(0.1, Math.min(1.0, (force - 1.0) / 2.0 + 0.5)); + return normalizedForce; + } + + // 增加檢測計數 + this.pressureCheckCount++; + + // Default pressure simulation with slight randomization + if (eventType === 'end') { + return 0.3; // 提筆時使用固定的低壓力值 + } + return 0.5 + Math.random() * 0.3; // Random pressure between 0.5 and 0.8 + } } // Export for use in other modules -window.PressureDrawing = PressureDrawing; \ No newline at end of file +window.PressureDrawing = PressureDrawing; diff --git a/pages/style.css b/pages/style.css index 96017ca..d4f0564 100644 --- a/pages/style.css +++ b/pages/style.css @@ -11,76 +11,386 @@ body { user-select: none; /* 禁用文字選擇 */ -webkit-touch-callout: none; /* 禁用長按彈出選單 */ -webkit-user-select: none; /* 禁用文字選擇 */ - -webkit-tap-highlight-color: transparent; /* 移除點擊高亮效果 */ + -webkit-tap-highlight-color: transparent; /* 移除點擊高亮效果 */ +} +button { + display: inline-block; + margin: 2px 0; + padding: 4px 6px; + font-size: 20px; +} +select { + margin: 2px 0; + padding: 4px 6px; + font-size: 16px; +} +span.butt { + display: inline-block; + margin: 2px 1px; + padding: 4px 6px; + font-size: 18px; + border: 1px solid #666; + border-radius: 10px; + cursor: pointer; + background-color: #eee; + color: #333; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } -button { display: inline-block; margin: 2px 0; padding: 4px 6px; font-size: 20px } -select { margin: 2px 0; padding: 4px 6px; font-size: 16px } -span.butt { display: inline-block; margin: 2px 1px; padding: 4px 6px; font-size: 18px; border: 1px solid #666; border-radius: 10px; cursor: pointer; background-color: #eee; color: #333; box-shadow: 0 2px 4px rgba(0,0,0,0.1) } -@font-face { font-family: LessonOne; src: url('LessonOne-Regular.woff'); font-weight: normal; font-style: normal; } -@font-face { font-family: GenYoExt; src: url('GenYoExt3-R.woff'); font-weight: normal; font-style: normal; } +@font-face { + font-family: LessonOne; + src: url('LessonOne-Regular.woff'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: GenYoExt; + src: url('GenYoExt3-R.woff'); + font-weight: normal; + font-style: normal; +} -#demo-container { margin-bottom: 5px; width: 360px; position: relative; color: #1123c4; text-align: center; font-family: sans-serif; height: 5.6em; margin: 10px auto } -#demo-container span { display: block; line-height: 1.2; } -#glyphName { font-family: Consolas, monospace; color: #aaa } -#demo-container #charSeq { font-size: 2.6em; line-height: 1; margin: 0 auto 10px auto; width: 1em; font-family: GenYoExt, LessonOne, sans-serif; border: 1px solid #aaa; } -#demo-container #charSeq.vert { writing-mode: vertical-rl; text-orientation: mixed; font-feature-settings: "vert" } -#prevButton { font-size: 3.2em; position: absolute; left: 0; top: 0; border: 0; background: transparent; box-shadow: none; } -#nextButton { font-size: 3.2em; position: absolute; right: 0; top: 0; border: 0; background: transparent; box-shadow: none } +#demo-container { + margin-bottom: 5px; + width: 360px; + position: relative; + color: #1123c4; + text-align: center; + font-family: sans-serif; + height: 5.6em; + margin: 10px auto; +} +#demo-container span { + display: block; + line-height: 1.2; +} +#glyphName { + font-family: Consolas, monospace; + color: #aaa; +} +#demo-container #charSeq { + font-size: 2.6em; + line-height: 1; + margin: 0 auto 10px auto; + width: 1em; + font-family: GenYoExt, LessonOne, sans-serif; + border: 1px solid #aaa; +} +#demo-container #charSeq.vert { + writing-mode: vertical-rl; + text-orientation: mixed; + font-feature-settings: 'vert'; +} +#prevButton { + font-size: 3.2em; + position: absolute; + left: 0; + top: 0; + border: 0; + background: transparent; + box-shadow: none; +} +#nextButton { + font-size: 3.2em; + position: absolute; + right: 0; + top: 0; + border: 0; + background: transparent; + box-shadow: none; +} -#canvas-container { position: relative; width: 360px; height: 360px; border: 1px solid #888; background-color: #fff;} -canvas { position: absolute; top: 0; left: 0; width: 360px; height: 360px; touch-action: none; } +#canvas-container { + position: relative; + width: 360px; + height: 360px; + border: 1px solid #888; + background-color: #fff; +} +canvas { + position: absolute; + top: 0; + left: 0; + width: 360px; + height: 360px; + touch-action: none; +} @media screen and (max-height: 750px), screen and (max-width: 380px) { - #canvas-container { width: 300px; height: 300px } - canvas { width: 300px; height: 300px; touch-action: none; } + #canvas-container { + width: 300px; + height: 300px; + } + canvas { + width: 300px; + height: 300px; + touch-action: none; + } +} +.smallmode { + width: 200px !important; + height: 200px !important; +} +.smallmode canvas { + width: 200px; + height: 200px; + touch-action: none; } -.smallmode { width: 200px !important; height: 200px !important} -.smallmode canvas { width: 200px; height: 200px; touch-action: none; } -#gridCanvas { z-index: 0 } /* 九宮格底圖在下方 */ -#drawingCanvas { position: absolute; top: 0; left: 0; z-index: 1 } /* 繪圖畫布在上方 */ +#gridCanvas { + z-index: 0; +} /* 九宮格底圖在下方 */ +#drawingCanvas { + position: absolute; + top: 0; + left: 0; + z-index: 1; +} /* 繪圖畫布在上方 */ -#slider-container { text-align: center; margin: 10px 0; font-family: sans-serif } -#slider-container .use { background-color: #ffc ;} -#lineWidthSlider { margin-left: 15px; width: 120px; display: inline-block; vertical-align: middle; } -#slider-container #lineWidthValue { display: inline-block; width: 1.2em; text-align: right; } -#brushSelector { display: inline-block; width: 32px; height: 32px; background-color: #fff; border: 1px solid #777; vertical-align: bottom; margin: 2px 0 2px 2px; padding: 0; cursor: pointer; border-radius: 10px 0 0 10px; text-align: center; } -#brushSelector img { width: 28px; height: 28px; line-height: 32px; margin-top: 2px} -#pressureButton { display: inline-block; width: 32px; height: 32px; border: 1px solid #777; margin: 2px 4px 2px 0; border-radius: 0 10px 10px 0; vertical-align: bottom; } -#slider-container .off { background: center no-repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY0ODQ0M2E4LTM5MzMtYjI0Yi04MDk1LTFlNzM4ZGNmNWUxNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjI5MmQzODI5LWNiZGEtZjE0NS05ZjVkLTRlOTM0Y2VhZjU4MCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjMxZDQ3YTdhLWUwNzEtOGI0Zi1hMTYwLWQzZDE3ZmYxNTQ5MyIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNTozNjo0MCswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2NDg0NDNhOC0zOTMzLWIyNGItODA5NS0xZTczOGRjZjVlMTYiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5CPmYRAAAMs0lEQVR42u1deVBV1xk/4AOBxyIgLiwqqCCLCBQEd8SKFVyC45K6JDbRVhON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'); background-size: 28px 28px; } -#slider-container .on { background: center no-repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjYyYjgyODhlLWQ1NDktM2E0OS1iNzEwLThkZGYyNTFhZDAxMSIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmVkMGNjMTc1LTA1ODQtMmI0OS1hNWMyLTgwNTg0ZWViNDc2YyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc0OTU2YTBlLWRiNDItNzg0MS04YmU4LWRiNmU3MGZkMTA0YSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNTozNzozNSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MmI4Mjg4ZS1kNTQ5LTNhNDktYjcxMC04ZGRmMjUxYWQwMTEiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6vtbYKAAAReklEQVR42u1dC1BU5xX+wRUfMSKCyIILIiCIwReCj1jBGoP1Ga1DTCzGREdr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'); background-size: 28px 28px; } -#penButton { padding-right: 2px; border-radius: 10px 0 0 10px; margin-right: 0 } -#eraserButton { padding-left: 2px; border-radius: 0 10px 10px 0; margin-left: 0; border-left: 0 } +#slider-container { + text-align: center; + margin: 10px 0; + font-family: sans-serif; +} +#slider-container .use { + background-color: #ffc; +} +#lineWidthSlider { + margin-left: 15px; + width: 120px; + display: inline-block; + vertical-align: middle; +} +#slider-container #lineWidthValue { + display: inline-block; + width: 1.2em; + text-align: right; +} +#brushSelector { + display: inline-block; + width: 32px; + height: 32px; + background-color: #fff; + border: 1px solid #777; + vertical-align: bottom; + margin: 2px 0 2px 2px; + padding: 0; + cursor: pointer; + border-radius: 10px 0 0 10px; + text-align: center; +} +#brushSelector img { + width: 28px; + height: 28px; + line-height: 32px; + margin-top: 2px; +} +#pressureButton { + display: inline-block; + width: 32px; + height: 32px; + border: 1px solid #777; + margin: 2px 4px 2px 0; + border-radius: 0 10px 10px 0; + vertical-align: bottom; +} +#slider-container .off { + background: center no-repeat + url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY0ODQ0M2E4LTM5MzMtYjI0Yi04MDk1LTFlNzM4ZGNmNWUxNiIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjI5MmQzODI5LWNiZGEtZjE0NS05ZjVkLTRlOTM0Y2VhZjU4MCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjMxZDQ3YTdhLWUwNzEtOGI0Zi1hMTYwLWQzZDE3ZmYxNTQ5MyIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNTozNjo0MCswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2NDg0NDNhOC0zOTMzLWIyNGItODA5NS0xZTczOGRjZjVlMTYiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6MzY6NDArMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5CPmYRAAAMs0lEQVR42u1deVBV1xk/4AOBxyIgLiwqqCCLCBQEd8SKFVyC45K6JDbRVhON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'); + background-size: 28px 28px; +} +#slider-container .on { + background: center no-repeat + url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyNS0wNy0xOVQxMToyNToxMSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjYyYjgyODhlLWQ1NDktM2E0OS1iNzEwLThkZGYyNTFhZDAxMSIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmVkMGNjMTc1LTA1ODQtMmI0OS1hNWMyLTgwNTg0ZWViNDc2YyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmE0ZTBhZjA2LTcyMTUtMWY0Ni1hODZkLWU3NzU4MzNmNmMwZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTRlMGFmMDYtNzIxNS0xZjQ2LWE4NmQtZTc3NTgzM2Y2YzBkIiBzdEV2dDp3aGVuPSIyMDI1LTA3LTE5VDExOjI1OjExKzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMCAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc0OTU2YTBlLWRiNDItNzg0MS04YmU4LWRiNmU3MGZkMTA0YSIgc3RFdnQ6d2hlbj0iMjAyNS0wNy0xOVQxNTozNzozNSswODowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MmI4Mjg4ZS1kNTQ5LTNhNDktYjcxMC04ZGRmMjUxYWQwMTEiIHN0RXZ0OndoZW49IjIwMjUtMDctMTlUMTU6Mzc6MzUrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6vtbYKAAAReklEQVR42u1dC1BU5xX+wRUfMSKCyIILIiCIwReCj1jBGoP1Ga1DTCzGREdrtM2YNplOk1jbJh0fSattktHEiRMNRh0fOBAjithG8AUaAiqKUVEQeQgIKqCI9Pvu7G3X9e6yT1Ry/5nlsXv33v8/3znf+c65/4JLc3OzUMejG66qCVQAVADUoQKgAqAOFQAVAHWoAKgAqEMFQAVAHSoAKgDqUAFQAVCHCoAKgDpUANr00PBLYmKi9IuLi4uorq4Wd+7ckX6+d++e2LRpk0hISBDJycnS7zdv3hRjx44V+/fvF4GBgeLcuXOiS5cuorKyUgwYMEA0NjaKW7duidLSUum4jIwMER4eLk6ePCn69esnOnToIC5duiTi4uLErl27xOXLl0VAQICIjY0V2dnZYtCgQSIoKEi4u7sLS26XNjU1SfPo1auX9LOzx+3bt6W1nThxQuzYscO1Xbt27f38/Dy6d+/ugzUGh4WFheTl5YU0NDQMhx1dQ0NDMzQazW7YaA/W3oTjxeTJk8X8+fP/D8CTPggUF9YaAOiH29WrV3XdunUbD8eLyc/P18HxQmpqajzpUIYDoIRibq96eHgcGzhw4NsAIqNNURANX1JSIsrLy6WfW2PA2M0wdrGnp+em+/fvLxg5cuT4iIiIZ5955plXdDrdAVdXV+Modb1+/fqIQ4cO7QfDzCbZtKkcQM8/e/aswCIFwr1VMGjfvv0dGL8W0Vft5uZWg9/P9ejRY2NMTMw4UOI0ePo14zfdvXu34/Hjx/+F93m3uSTMnAUqEGVlZa0WCYYUyAdzJB5MXEnIZWOVjkVu6JqTkxPbJlUQDV9QUCDAz60VCQ+Nrl27isjISAqDfAiL7UrH5ObmTmyzMpT8e+HCBVFVVdVqIJACQS8SBVKRRUVFCeQEqr7PlI5H4h70gAxtc9oahicdBQcHC29vb6eqI1KPVqsVvr6+khigPJXlMxLubVKjsZyura11b9MAyIYhHfF7z549nQICjQslJAGAOkBABT1gbFzfOz09/WEN6+bW1OYBoHH4IAj8DoUioD4cdn6eq66uTvL8gwcPKp4bResEExFa1eYBMEzMzAmQiZKXUqk44px9+/aVeJ90p5RreMz3338/Q+n9KODO/GQAkL311KlTUivEy8tL2LojnJFEY6MIkx4+Pj4mj+3YsaPPjRs3PJRee/rpp7/7SQFAw9Ej2bdin8vSPpMSkFlZWVJEsSdkqt7QR91EU+cJCAj4zyMFgFKRlEDDGHpWawDBipnFmnG7wNLEXl9fLzUUO3XqZM77xZEjR15Wuj5UWWFQUND1VgWAHiE/sHBvJK/gK1euBGAx3vCqDvh+FxKuEK9nA5hiZ84F4W8zAHKhZUGkdMV6BigB6Ofnl/XUU0/VOh0Aog25xYVqwJfRUATjs7OzR4ICwqCPfeGJ/2tIAQzpAeNcx2t75s2btwjvu+VI1WI4mIgrKir+F4GOHtD5z2KdikiNGzfufTiZc2SozLWgE7rXYKiAFy5dujQVUjC8sbGxndLxhlwMkLzwmJ2YmOi7ePHicQ0NDU5xDA8Pjwco0MFFoEtmZuZvsV4349cGDx68D/knlxHocAC4GHgt++SzEH6vlZeXD0ci0hhFxA3w4yWohywcWwJPLB8wYED90aNHdaCl0fD+sTjOJS8v77l169atCQ0NfcPRn2Gj98MQUqvA0RFG50Mkj/rxxx/HK70+atSoP7IgZA5xGAB6w3sD8Xgg/zqM2M9wYfC2a3h8A6MnQ5bloCC6ChCaSE/0hCFDhojz588LGJt99t8dOnToQ74PkTMpJCTkAxxX7ugIwDykRClXx44Cmflh48aNHym9BkfbhLrhRGFh4YMRY0+VCcMzgb5YWlr6EenDoNRugN7eA929FYs8gAVWoQhq5u1ODt62lJtYlIUEbMyYMaJz585rT58+/UZVVZUO9ON/+fJld1CFwwCgoZmEqWSKioqEv7+/zclYySaI5AWg2ygFVXQrMjJyDUF/iLJsuRANBgO9jAv+obKyMoK/83lIs0oscN306dM/g16+Am9v5kL5mlIvRr6VyM5lWloa6aEBVeJ3+H0WjKTp3bu3F4A770gAALL44YcfZIOJGTNmSHWBveeFd3f79NNP31Rqvul0ujxE+gkCbxcA9BY8fFNSUj65ePHiC/LzkFXFvr6+60Eza8H/ZVwQvbylBhiNwRYuK0reyOdaAGyj/DoiS4eC54gjJSivw5qDwLOXA6EgBg4cKDmVrQ07rmPv3r3vQO31VXo9Pj5+CesGJn6rASCaNDyzOyjkFcjHFaAN6ZYaTnonMDBwI0LrLSSWGrlalCOipUiiRwBI0adPH+k6eK49AOjDtq5eNZSxk+moBMlcQ6MbzgEJU0rMzEkEgpRozeD7IK+fTU5O/r3S6/37918TFhZ2jJWzomqyZOIIL5/c3NzPAMBEGNeVEw8ODk7RarV/As+f5NYT2assDFcpCdIjoZmlhx68DuD9oXK0HThwoNxQMdjTC2LXkvM2phuen8bhthpubSHg1iRlrEUD4bBC6TVQcOHcuXOXcy1gCesA4CSILsIyZuXKlduhHLz04XYbnrkE5fTn3N9jTXeRIc7JUIczJMH3D1wPIxRKqrMcXUjkjUpha+2ggYuLiwVyykPGlSOcUcc+z7Rp06QokYWCOTrWtxziEUUjTBRdiwF+qRzRVgFAXjtz5kxcfn7+VzCcF70nICAgEx4yD9/PWhuq9EJ6QUxMjLRAY0MwgsDHYwwWWIsEfMfeCOC8aQBzvRs50hmRu3fvJm1IG8Tkm+1KdMpuKOg4bNu2bYkmZOfq8PDwb7iJy2zhZqqZBIXw6pdffvmF/BxCeGlsbOyqnJycBmsLGC6CBhg2bJjkWYwspWtispNlo8HwlYiAOnsAYPRcu3bN4oqXx3N3np+fnxSlxgDIP/M7ahifzZs3J5lw3oqpU6d+oO/9twyArIX1UlKkpqa+uX379lX6Qqq6b9++sxEFKTSeLeU7F8bth3I3UWnQ0JCHowzkaSVCu86eapXXpdREErSI13kM58E8sXTpUqlijo6OFhs2bJDsMmLECGn+4H3XrVu3bquoqAhViLhmSNs3AOJ1S64pASAbhTSAJBiOsPpI/3s9eGwmku8+Nq9s0cfMEdwbSvlnakJ0APDvQsPnQD9lqI7rbZWGLPrY/6dBra109XaQCjUKBjYKuf+V+2I5H0TJe4jWnym9FyD9DZr/a57DYgB4J1+f0XXw/J16TqybN2/e87hwJhWCtYMT5STo+aQcc5Oh/ITKetHwOXjcd9zZYEubgPd/WV/YWuXymowe5gVGvPwzwGx36tSpt5F4lym9D4pwV0JCwnuy0rMIbH4JCQmRjLV8+fKPoXZCsfjb4PuZkJqZRJ8ntIYKeCz7IlQdTGwtKSVEhxbXDTd8LiIi4lt6nC2KxxmtZjoRovQ1SO6/KjkF1lk8YcKEX2PtzdY4jUY2GAqiSVevXp2i79otQfinUOnId68YypYsSi5qCCqbXnL/x9yAtwZBVfQwVC7w4gIqMWsVD1vYlMeO6vHIFAknWQCmWGtCtDQsXLgwBg5Xbq061OiTVYfVq1fv1rcVCuPi4j6nJxEY7iSgepG1srnQ4vFUD3wPjWcRByLyUKE+sHsAyuFaUlKSVVHH+cl05yjjc/5UZ1VVVRPgICtNXTcqKuoVzPmipbTzEABQChNwMcm9Ubn9hnrdUP7J3U8alWqAey/JiYYGprG4+FmzZknflaSm0gK5sQnXn2X4vLe393nur2ypGDKcH6mS82buMaXdbenxpKenL0AOXGvqmJEjR341ffr0nbZuCJYAQLIbzHWANg4D8YPszygtQJap3O5HfpYpgoum3uVeGU7ElNRUKn6OHz8ej8LM3SgqrliqIngOzoXGd1Rfn9fGWl2RcP9+8uTJxUp24LVQpyTB4RJIu7ZeW6MP+T56LktFdXebPGouccpoy7KME2bC5cOa24gM3y1btvzFuIWLhV1hX8aSCOD18vLypP0+juJ7SMxeoJwNiPTnlGiQc4WzJsFeCaQdfZfYdgBwEunDBCgeuk2cOFHs27dP6p20dFK+ztCnzmezi0nXmoFqMhS5poex90CDF7GCtYQiZIFgr/fz/XQkONTAnTt3fl1bW9vP1LFQeAVwtngIjEa2VWSKthkAoJmWmZnJ24GLcaJ9oJC9lrYA6KUEgdWmtTc2oH7CsYDuxs8jqRWb41TDlgB7LZZSXgtVsxsoZxGA/xBrcjVFOwA9HyrxebBGI/MXb+7YRXf8AvWQOmjQoNdBP+9C6/5j6NChReDU05Z6FcOUQBj22i1JnJj8fKVroNK8YS766Kns18jncQDfa7H2XfD6YabWzOfh+XmoT+LgnNcclm9kL0YhsRaRkIgTu8Eb6plQLW0DsM1AZWSNMaCSuh47dkxx9wAS/X1zALDIY4Raq7mNDY9He9DggsOHD7+LfGb2zg+Ex7HAwMDRmPddR+6m0MhexJMi8d6UF84kbMmFDLcVWuoV+u0bcWZucriYApPX4C1MW43Aa8PBNBcuXHgeBdufQYNDza2N12NdMmfOnJfT0tLuOnqbjF3bUjhBRg8/ImrN3k6CjIVPMvV6//79uyvlANYWskKztEYwdhTQpA7SciO4Pla+dapkVPl5RNvZKVOmjIMqK3bG39m2GwDKMGpwa+iH1SW8b5QZigmZOXPmA5KWoEEkSDuTrfV+vhd0qs3JyXkLVDkbIHgaRpSpSNNqtek6nS4BlFjirI852QUAPZFcbO1NEwDmA871M/V6bm5uOOWl7OUEl/eNeQOdf8bAUqPru5k98N7FAG4RcoanpTQFJ9kMZbcA83TaHlW7AYBHCVv6H6CSXry5byYH+LMXRQBoSALA3r6pnQUKHE9hMLSkpOQl1Ca/QqL1tqYkGD169Bo4wRLRCsMmALhIbrFj19GWm+ZUH+b4FAk6GrwreT2rbe7pZ5SZ0vtyxxbfOyG3/KKoqGgGjB+PCG1n5bqa4PXvAIAP6VyPLQD6e6JS78eWAWM2FBQUmEQAnu4OCvjlpEmTdixbtuyhily/LVLI/SKAFI3KfSoe8fg52BbKgNIpQi20CNdObs0Pedt0JS5w+PDhNl+0S5cuxXv37r1vKrHRwOvXr/8AtHMcvxbRIMw3sqfj566IhlD2avDzzxExsTiXzVaD8fMANrfDlzHqWvO/imhs8X4WXfbs3UcEVHDnQE1Njc7MNUJTU1Mz3N3dv0WRWIgE6gY+73706NFQGD+kurq6DxzBrjIYQN+H8ZdHR0evAIC1rfjnbmwHgJPUqwS7qlBUlRng2ZfMHQcj++OxwPA5buR1xACwWfHx8a9nZGRki0c4rAZA3sBkTw+GVAI5+VVLADhlwRpNU1RU1CI/P78v/P39G6m0HuU/MtJY6/38OL4t0tN4DBkyZE9mZmZ+aWlpv9ZYKHMHqtkkLy+v9319fU8Y1hlPVASY2mZty5gzZ87cVatWpQHYzs5cpIeHR7aPj8+yiIiIb7hdhQn9Uf05G7sAoPrhjRoHVoZHEAlvZ2VlfezohZHmPD09C3Q63T9h/I2oW24ygp1Z1ToVAPlTLvxAg6N2HfA8kZGRn8Ar+Umaj+GZdt9Jhzioh8dn9e7dewW4/t+oaOscFbGPBQU5+uOdzCdQJGtBD4VQJO9Dmg62Rg7qNwpUwei5KAy/5Y1ySNbz0PPN8l9SfJz/W+BjQYTw/PuQpXvc3NzSUWE/hxpgLuTnkLq6Oh/qf6McdA+GLe/Zs2cxXs8ivwPAdK1We4V/rYqfSZOp5kn4N42PBQDyfQUYrAHGTIFxUyoqKgLw3begoEALr+6JyLsHMK6DyytQPJXFxcVd3rJly13uhuB7aXDeIXvS/jemi/rPPB/tUP92tAqACoA6VABUANShAqACoA4VABUAdagAqACoQwVABUAdKgAqAOpQAVABUIcKQNse/wXWGpcwRunodQAAAABJRU5ErkJggg=='); + background-size: 28px 28px; +} +#penButton { + padding-right: 2px; + border-radius: 10px 0 0 10px; + margin-right: 0; +} +#eraserButton { + padding-left: 2px; + border-radius: 0 10px 10px 0; + margin-left: 0; + border-left: 0; +} -#button-container { display: flex; width: 320px; gap: 10px; justify-content: space-between } -#button-container span.butt { padding: 0; border: 0; font-size: 32px; box-shadow: none; line-height: 1.1; } +#button-container { + display: flex; + width: 320px; + gap: 10px; + justify-content: space-between; +} +#button-container span.butt { + padding: 0; + border: 0; + font-size: 32px; + box-shadow: none; + line-height: 1.1; +} -#list-container { display: block; position: fixed; top: 0; left: 0; width: 100%; justify-content: center; background-color: #fff; padding: 10px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); text-align: center; z-index: 1; } +#list-container { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + justify-content: center; + background-color: #fff; + padding: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + text-align: center; + z-index: 1; +} -#navi-container { display: flex; gap: 10px; position: fixed; bottom: 0; left: 0; width: 100%; justify-content: center; background-color: #fff; padding: 10px; box-shadow: 0 -2px 5px rgba(0,0,0,0.1); } -#navi-container #spanDoneCount { padding-top: 12px; color: #999; width: 3em; text-align: center; } -#navi-container #label-tester { text-align: center; font-size: 0.8em } -#navi-container #label-tester input { width: 1.2em; height: 1.2em } -#progress-container { display: none; position: fixed; bottom: 0; left: 0; width: 100%; background-color: #fff; padding: 10px; text-align: center; box-shadow: 0 -2px 5px rgba(0,0,0,0.1); z-index: 1000; } -#progress-bar { width: 80%; height: 20px; display: block; margin: 0 auto; } +#navi-container { + display: flex; + gap: 10px; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + justify-content: center; + background-color: #fff; + padding: 10px; + box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); +} +#navi-container #spanDoneCount { + padding-top: 12px; + color: #999; + width: 3em; + text-align: center; +} +#navi-container #label-tester { + text-align: center; + font-size: 0.8em; +} +#navi-container #label-tester input { + width: 1.2em; + height: 1.2em; +} +#progress-container { + display: none; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + background-color: #fff; + padding: 10px; + text-align: center; + box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); + z-index: 1000; +} +#progress-bar { + width: 80%; + height: 20px; + display: block; + margin: 0 auto; +} -.dialog { display: none; position: fixed; top: 0; left: 0; width: 90%; height: 100%; background-color: rgba(0, 0, 0, 0.8); color: #fff; z-index: 999; text-align: left; padding: 0 5% } -.dialog h2 { margin-top: 1em } -.dialog h3 { display: block; font-size: 1.2em; margin: 0.8em 0 0.2em 0; } -.dialog a { color: #ff0 !important; text-decoration: underline; } -.dialog input[type="text"] { width: 80%; padding: 3px; font-size: 1.2em; } -.dialog input[type="file"] { width: 90%; padding: 3px; font-size: 1.2em; } -.dialog input[type="range"] { width: 80%; vertical-align: middle; } -.dialog input[type="checkbox"] { width: 2em; height: 2em; vertical-align: middle } -.dialog .note { display: block; color: #ccc } +.dialog { + display: none; + position: fixed; + top: 0; + left: 0; + width: 90%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + color: #fff; + z-index: 999; + text-align: left; + padding: 0 5%; +} +.dialog h2 { + margin-top: 1em; +} +.dialog h3 { + display: block; + font-size: 1.2em; + margin: 0.8em 0 0.2em 0; +} +.dialog a { + color: #ff0 !important; + text-decoration: underline; +} +.dialog input[type='text'] { + width: 80%; + padding: 3px; + font-size: 1.2em; +} +.dialog input[type='file'] { + width: 90%; + padding: 3px; + font-size: 1.2em; +} +.dialog input[type='range'] { + width: 80%; + vertical-align: middle; +} +.dialog input[type='checkbox'] { + width: 2em; + height: 2em; + vertical-align: middle; +} +.dialog .note { + display: block; + color: #ccc; +} -.close { position: absolute; display: block; right: 20px; top: 15px; font: normal 3em/1 sans-serif} -.dialog .dummy { display: block; clear: both; height: 3em } -.dialog-body { height: 100%; overflow: scroll; max-width: 720px; margin: 0 auto; position: relative; } +.close { + position: absolute; + display: block; + right: 20px; + top: 15px; + font: normal 3em/1 sans-serif; +} +.dialog .dummy { + display: block; + clear: both; + height: 3em; +} +.dialog-body { + height: 100%; + overflow: scroll; + max-width: 720px; + margin: 0 auto; + position: relative; +} -#listup-body svg { display: block; background-color: #fff; width: 50px; height: 50px; float: left; border: 1px solid #ccc; margin: 5px } -#listup-body span { display: block; background-color: #666; color: #ddd; width: 50px; height: 50px; float: left; border: 1px solid #ccc; line-height: 50px; text-align: center; font-size: 2em; margin: 5px; font-family: GenYoExt, LessonOne, sans-serif; overflow: hidden; } -#listup-body span.vert { writing-mode: vertical-rl; text-orientation: mixed; font-feature-settings: "vert" } +#listup-body svg { + display: block; + background-color: #fff; + width: 50px; + height: 50px; + float: left; + border: 1px solid #ccc; + margin: 5px; +} +#listup-body span { + display: block; + background-color: #666; + color: #ddd; + width: 50px; + height: 50px; + float: left; + border: 1px solid #ccc; + line-height: 50px; + text-align: center; + font-size: 2em; + margin: 5px; + font-family: GenYoExt, LessonOne, sans-serif; + overflow: hidden; +} +#listup-body span.vert { + writing-mode: vertical-rl; + text-orientation: mixed; + font-feature-settings: 'vert'; +} -#hintContent { padding: 0 1.2em } -#hintContent li { margin: 10px 0; line-height: 1.6; } +#hintContent { + padding: 0 1.2em; +} +#hintContent li { + margin: 10px 0; + line-height: 1.6; +}