add font customization

This commit is contained in:
Bruce Liu 2021-12-16 15:00:48 +08:00
parent cf38cc00c8
commit bba895a95e
21 changed files with 426 additions and 671 deletions

View File

@ -1,10 +1,3 @@
# Build the MAS app
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder -c electron-builder-mas.yml --mac mas:universal
# Add ElectronTeamID to Info.plist
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>EM8VE646TZ<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"
printf "......................\nresignAndPackage start\n\n"
# Name of your app. # Name of your app.
APP="Fluent Reader" APP="Fluent Reader"
# Your Certificate name. # Your Certificate name.
@ -21,6 +14,17 @@ PARENT_PLIST="build/entitlements.mas.plist"
CHILD_PLIST="build/entitlements.mas.inherit.plist" CHILD_PLIST="build/entitlements.mas.inherit.plist"
LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist" LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist"
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks" FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
# Build universal binary for font-list
# FONTLIST_PATH="node_modules/font-list/libs/darwin/fontlist.m"
# clang -arch arm64 -arch x86_64 "$FONTLIST_PATH" -fmodules -o "dist/fontlist"
# Build the MAS app
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder -c electron-builder-mas.yml --mac mas:universal
# Add ElectronTeamID to Info.plist
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>EM8VE646TZ<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"
printf "......................\nresignAndPackage start\n\n"
codesign --deep --force --verify --verbose=4 --timestamp --options runtime --entitlements "$CHILD_PLIST" -s "$APP_KEY" "$APP_PATH/Contents/Resources/app.asar.unpacked/dist/fontlist"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib"
@ -44,4 +48,4 @@ codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacO
codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH" codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH"
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH" productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"
printf "\nresignAndPackage end\n......................\n" printf "\nresignAndPackage end\n......................\n"

View File

@ -128,6 +128,11 @@ article code {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1; line-height: 1;
} }
article pre {
word-break: normal;
overflow-wrap: normal;
white-space: pre-wrap;
}
article blockquote { article blockquote {
border-left: 2px solid var(--gray); border-left: 2px solid var(--gray);
margin: 1em 0; margin: 1em 0;

View File

@ -3,14 +3,14 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" <meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src-elem 'sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=' 'sha256-OOzJH14UHiDzBx2LDF/KOAR+BVrHdNzZ2fZvPaqCMhY='; img-src http: https: data:; style-src 'self' 'unsafe-inline'; frame-src http: https:; media-src http: https:; connect-src https: http:"> content="default-src 'none'; script-src-elem 'sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=' 'sha256-iOdZeo0zvgcSuiH/7/dXCOHo7s0cn2XtsidqVOcHBjo='; img-src http: https: data:; style-src 'self' 'unsafe-inline'; frame-src http: https:; media-src http: https:; connect-src https: http:">
<title>Article</title> <title>Article</title>
<link rel="stylesheet" href="article.css" /> <link rel="stylesheet" href="article.css" />
<script integrity="sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=" src="mercury.web.js"></script> <script integrity="sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=" src="mercury.web.js"></script>
</head> </head>
<body> <body>
<div id="main"></div> <div id="main"></div>
<script integrity="sha256-OOzJH14UHiDzBx2LDF/KOAR+BVrHdNzZ2fZvPaqCMhY=" src="article.js"></script> <script integrity="sha256-iOdZeo0zvgcSuiH/7/dXCOHo7s0cn2XtsidqVOcHBjo=" src="article.js"></script>
<!-- Run "cat article.js | openssl dgst -sha256 -binary | openssl enc -base64 -A" for hash --> <!-- Run "cat article.js | openssl dgst -sha256 -binary | openssl enc -base64 -A" for hash -->
</body> </body>
</html> </html>

View File

@ -20,6 +20,8 @@ async function getArticle(url) {
} }
} }
document.documentElement.style.fontSize = get("s") + "px" document.documentElement.style.fontSize = get("s") + "px"
let font = get("f")
if (font) document.body.style.fontFamily = `"${font}"`
let url = get("u") let url = get("u")
getArticle(url).then(article => { getArticle(url).then(article => {
let domParser = new DOMParser() let domParser = new DOMParser()

BIN
dist/fontlist vendored Executable file

Binary file not shown.

29
dist/fonts.vbs vendored Normal file
View File

@ -0,0 +1,29 @@
Option Explicit
Dim objShell, objFSO, objFile, objFolder
Dim objFolderItem, colItems, objFont
Dim strFileName
Const FONTS = &H14& ' Fonts Folder
' Instantiate Objects
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(FONTS)
Set objFolderItem = objFolder.Self
Set colItems = objFolder.Items
Set objFSO = CreateObject("Scripting.FileSystemObject")
For Each objFont in colItems
WScript.StdOut.WriteLine(objFont.Path & vbtab & objFont.Name)
Next
Set objShell = nothing
Set objFile = nothing
Set objFolder = nothing
Set objFolderItem = nothing
Set colItems = nothing
Set objFont = nothing
Set objFSO = nothing
wscript.quit

View File

@ -4,7 +4,10 @@ productName: Fluent Reader
copyright: Copyright © 2020 Haoyuan Liu copyright: Copyright © 2020 Haoyuan Liu
files: files:
- "./dist/**/*" - "./dist/**/*"
- "!./dist/fonts.vbs"
- "!**/*.js.map" - "!**/*.js.map"
asarUnpack:
- "./dist/fontlist"
directories: directories:
output: "./bin/${platform}/${arch}/" output: "./bin/${platform}/${arch}/"
mac: mac:

View File

@ -3,6 +3,7 @@ productName: Fluent Reader
copyright: Copyright © 2020 Haoyuan Liu copyright: Copyright © 2020 Haoyuan Liu
files: files:
- "./dist/**/*" - "./dist/**/*"
- "!./dist/fontlist"
- "!**/*.js.map" - "!**/*.js.map"
directories: directories:
output: "./bin/${platform}/${arch}/" output: "./bin/${platform}/${arch}/"

948
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@
"electron-react-devtools": "^0.5.3", "electron-react-devtools": "^0.5.3",
"electron-store": "^5.2.0", "electron-store": "^5.2.0",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"font-list": "^1.4.2",
"hard-source-webpack-plugin": "^0.13.1", "hard-source-webpack-plugin": "^0.13.1",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.3.0",
"js-md5": "^0.7.3", "js-md5": "^0.7.3",

View File

@ -75,6 +75,13 @@ const settingsBridge = {
ipcRenderer.invoke("set-font-size", size) ipcRenderer.invoke("set-font-size", size)
}, },
getFont: (): string => {
return ipcRenderer.sendSync("get-font")
},
setFont: (font: string) => {
ipcRenderer.invoke("set-font", font)
},
getFetchInterval: (): number => { getFetchInterval: (): number => {
return ipcRenderer.sendSync("get-fetch-interval") return ipcRenderer.sendSync("get-fetch-interval")
}, },

View File

@ -170,11 +170,16 @@ const utilsBridge = {
destroyTouchBar: () => { destroyTouchBar: () => {
ipcRenderer.invoke("touchbar-destroy") ipcRenderer.invoke("touchbar-destroy")
}, },
initFontList: (): Promise<Array<string>> => {
return ipcRenderer.invoke("init-font-list")
},
} }
declare global { declare global {
interface Window { interface Window {
utils: typeof utilsBridge utils: typeof utilsBridge
fontList: Array<string>
} }
} }

View File

@ -38,6 +38,7 @@ type ArticleProps = {
} }
type ArticleState = { type ArticleState = {
fontFamily: string
fontSize: number fontSize: number
loadWebpage: boolean loadWebpage: boolean
loadFull: boolean loadFull: boolean
@ -53,7 +54,8 @@ class Article extends React.Component<ArticleProps, ArticleState> {
constructor(props: ArticleProps) { constructor(props: ArticleProps) {
super(props) super(props)
this.state = { this.state = {
fontSize: this.getFontSize(), fontFamily: window.settings.getFont(),
fontSize: window.settings.getFontSize(),
loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage, loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage,
loadFull: props.source.openTarget === SourceOpenTarget.FullContent, loadFull: props.source.openTarget === SourceOpenTarget.FullContent,
fullContent: "", fullContent: "",
@ -68,15 +70,16 @@ class Article extends React.Component<ArticleProps, ArticleState> {
this.loadFull() this.loadFull()
} }
getFontSize = () => {
return window.settings.getFontSize()
}
setFontSize = (size: number) => { setFontSize = (size: number) => {
window.settings.setFontSize(size) window.settings.setFontSize(size)
this.setState({ fontSize: size }) this.setState({ fontSize: size })
} }
setFont = (font: string) => {
window.settings.setFont(font)
this.setState({ fontFamily: font })
}
fontMenuProps = (): IContextualMenuProps => ({ fontSizeMenuProps = (): IContextualMenuProps => ({
items: FONT_SIZE_OPTIONS.map(size => ({ items: FONT_SIZE_OPTIONS.map(size => ({
key: String(size), key: String(size),
text: String(size), text: String(size),
@ -86,6 +89,16 @@ class Article extends React.Component<ArticleProps, ArticleState> {
})), })),
}) })
fontFamilyMenuProps = (): IContextualMenuProps => ({
items: window.fontList.map((font, idx) => ({
key: String(idx),
text: font === "" ? intl.get("default") : font,
canCheck: true,
checked: this.state.fontFamily === font,
onClick: () => this.setFont(font)
}))
})
updateTextDirection = (direction: SourceTextDirection) => { updateTextDirection = (direction: SourceTextDirection) => {
this.props.updateSourceTextDirection(this.props.source, direction) this.props.updateSourceTextDirection(this.props.source, direction)
} }
@ -154,10 +167,17 @@ class Article extends React.Component<ArticleProps, ArticleState> {
}, },
{ {
key: "fontMenu", key: "fontMenu",
text: intl.get("article.font"),
iconProps: { iconName: "Font" },
disabled: this.state.loadWebpage,
subMenuProps: this.fontFamilyMenuProps(),
},
{
key: "fontSizeMenu",
text: intl.get("article.fontSize"), text: intl.get("article.fontSize"),
iconProps: { iconName: "FontSize" }, iconProps: { iconName: "FontSize" },
disabled: this.state.loadWebpage, disabled: this.state.loadWebpage,
subMenuProps: this.fontMenuProps(), subMenuProps: this.fontSizeMenuProps(),
}, },
{ {
key: "directionMenu", key: "directionMenu",
@ -334,7 +354,9 @@ class Article extends React.Component<ArticleProps, ArticleState> {
</> </>
) )
) )
return `article/article.html?a=${a}&h=${h}&s=${ return `article/article.html?a=${a}&h=${h}&f=${
encodeURIComponent(this.state.fontFamily)
}&s=${
this.state.fontSize this.state.fontSize
}&d=${ }&d=${
this.props.source.textDir this.props.source.textDir

View File

@ -26,6 +26,11 @@ window.utils.addMainContextListener((pos, text) => {
store.dispatch(openTextMenu(pos, text)) store.dispatch(openTextMenu(pos, text))
}) })
window.fontList = [""]
window.utils.initFontList().then((fonts) => {
window.fontList.push(...fonts)
})
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<Root /> <Root />

View File

@ -122,6 +122,14 @@ ipcMain.handle("set-font-size", (_, size: number) => {
store.set(FONT_SIZE_STORE_KEY, size) store.set(FONT_SIZE_STORE_KEY, size)
}) })
const FONT_STORE_KEY = "fontFamily"
ipcMain.on("get-font", event => {
event.returnValue = store.get(FONT_STORE_KEY, "")
})
ipcMain.handle("set-font", (_, font: string) => {
store.set(FONT_STORE_KEY, font)
})
ipcMain.on("get-all-settings", event => { ipcMain.on("get-all-settings", event => {
let output = {} let output = {}
for (let [key, value] of store) { for (let [key, value] of store) {

View File

@ -10,6 +10,7 @@ import { WindowManager } from "./window"
import fs = require("fs") import fs = require("fs")
import { ImageCallbackTypes, TouchBarTexts } from "../schema-types" import { ImageCallbackTypes, TouchBarTexts } from "../schema-types"
import { initMainTouchBar } from "./touchbar" import { initMainTouchBar } from "./touchbar"
import fontList = require('font-list')
export function setUtilsListeners(manager: WindowManager) { export function setUtilsListeners(manager: WindowManager) {
async function openExternal(url: string, background = false) { async function openExternal(url: string, background = false) {
@ -283,4 +284,10 @@ export function setUtilsListeners(manager: WindowManager) {
ipcMain.handle("touchbar-destroy", () => { ipcMain.handle("touchbar-destroy", () => {
if (manager.hasWindow()) manager.mainWindow.setTouchBar(null) if (manager.hasWindow()) manager.mainWindow.setTouchBar(null)
}) })
ipcMain.handle("init-font-list", () => {
return fontList.getFonts({
disableQuoting: true,
})
})
} }

View File

@ -88,6 +88,7 @@ export type SchemaTypes = {
locale: string locale: string
sourceGroups: SourceGroup[] sourceGroups: SourceGroup[]
fontSize: number fontSize: number
fontFamily: string
menuOn: boolean menuOn: boolean
fetchInterval: number fetchInterval: number
searchEngine: SearchEngines searchEngine: SearchEngines

View File

@ -18,6 +18,7 @@
"confirmMarkAll": "Do you really want to mark all articles on this page as read?", "confirmMarkAll": "Do you really want to mark all articles on this page as read?",
"confirm": "Confirm", "confirm": "Confirm",
"cancel": "Cancel", "cancel": "Cancel",
"default": "Default",
"time": { "time": {
"now": "now", "now": "now",
"m": "m", "m": "m",
@ -70,7 +71,8 @@
"textDir": "Text direction", "textDir": "Text direction",
"LTR": "Left-to-right", "LTR": "Left-to-right",
"RTL": "Right-to-left", "RTL": "Right-to-left",
"Vertical": "Vertical" "Vertical": "Vertical",
"font": "Font"
}, },
"context": { "context": {
"share": "Share", "share": "Share",

View File

@ -18,6 +18,7 @@
"confirmMarkAll": "确认将本页所有文章标为已读?", "confirmMarkAll": "确认将本页所有文章标为已读?",
"confirm": "确认", "confirm": "确认",
"cancel": "取消", "cancel": "取消",
"default": "默认",
"time": { "time": {
"now": "now", "now": "now",
"m": "m", "m": "m",
@ -70,7 +71,8 @@
"textDir": "文本方向", "textDir": "文本方向",
"LTR": "从左到右", "LTR": "从左到右",
"RTL": "从右到左", "RTL": "从右到左",
"Vertical": "纵书" "Vertical": "纵书",
"font": "字体"
}, },
"context": { "context": {
"share": "分享", "share": "分享",

View File

@ -18,6 +18,7 @@
"confirmMarkAll": "確認將本頁所有文章標為已讀?", "confirmMarkAll": "確認將本頁所有文章標為已讀?",
"confirm": "確認", "confirm": "確認",
"cancel": "取消", "cancel": "取消",
"default": "預設",
"time": { "time": {
"now": "now", "now": "now",
"m": "m", "m": "m",
@ -70,7 +71,8 @@
"textDir": "文本方向", "textDir": "文本方向",
"LTR": "從左到右", "LTR": "從左到右",
"RTL": "從右到左", "RTL": "從右到左",
"Vertical": "縱書" "Vertical": "縱書",
"font": "字體"
}, },
"context": { "context": {
"share": "分享", "share": "分享",

View File

@ -23,6 +23,9 @@ module.exports = [
path: __dirname + "/dist", path: __dirname + "/dist",
filename: "electron.js", filename: "electron.js",
}, },
node: {
__dirname: false
},
plugins: [new HardSourceWebpackPlugin()], plugins: [new HardSourceWebpackPlugin()],
}, },
{ {