mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
Feat/Added label maker custom font (#1038)
* Add label maker font config * Add document for label maker font config * Add test for custom font * Fix custom font setup documentation - Fallback font is gofont which don't support CJK characters * Fix golangci-lint error * Update custom-font-setup.md * Fix typo
This commit is contained in:
@@ -71,6 +71,8 @@ type LabelMakerConf struct {
|
|||||||
DynamicLength bool `yaml:"bool" conf:"default:true"`
|
DynamicLength bool `yaml:"bool" conf:"default:true"`
|
||||||
LabelServiceUrl *string `yaml:"label_service_url"`
|
LabelServiceUrl *string `yaml:"label_service_url"`
|
||||||
LabelServiceTimeout *time.Duration `yaml:"label_service_timeout"`
|
LabelServiceTimeout *time.Duration `yaml:"label_service_timeout"`
|
||||||
|
RegularFontPath *string `yaml:"regular_font_path"`
|
||||||
|
BoldFontPath *string `yaml:"bold_font_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BarcodeAPIConf struct {
|
type BarcodeAPIConf struct {
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ import (
|
|||||||
"golang.org/x/image/font/gofont/gomedium"
|
"golang.org/x/image/font/gofont/gomedium"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FontType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
FontTypeRegular FontType = iota
|
||||||
|
FontTypeBold
|
||||||
|
)
|
||||||
|
|
||||||
type GenerateParameters struct {
|
type GenerateParameters struct {
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
@@ -140,6 +147,48 @@ func wrapText(text string, face font.Face, maxWidth int, maxHeight int, lineHeig
|
|||||||
return wrappedLines, ""
|
return wrappedLines, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadFont(cfg *config.Config, fontType FontType) (*truetype.Font, error) {
|
||||||
|
var fontPath *string
|
||||||
|
var fallbackData []byte
|
||||||
|
|
||||||
|
switch fontType {
|
||||||
|
case FontTypeRegular:
|
||||||
|
if cfg != nil && cfg.LabelMaker.RegularFontPath != nil {
|
||||||
|
fontPath = cfg.LabelMaker.RegularFontPath
|
||||||
|
}
|
||||||
|
fallbackData = gomedium.TTF
|
||||||
|
case FontTypeBold:
|
||||||
|
if cfg != nil && cfg.LabelMaker.BoldFontPath != nil {
|
||||||
|
fontPath = cfg.LabelMaker.BoldFontPath
|
||||||
|
}
|
||||||
|
fallbackData = gobold.TTF
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown font type: %d", fontType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fontPath != nil && *fontPath != "" {
|
||||||
|
data, err := os.ReadFile(*fontPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to load font from %s: %v, using fallback font", *fontPath, err)
|
||||||
|
} else {
|
||||||
|
font, err := truetype.Parse(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to parse font from %s: %v, using fallback font", *fontPath, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Successfully loaded font from %s", *fontPath)
|
||||||
|
return font, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := truetype.Parse(fallbackData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return font, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateLabel(w io.Writer, params *GenerateParameters, cfg *config.Config) error {
|
func GenerateLabel(w io.Writer, params *GenerateParameters, cfg *config.Config) error {
|
||||||
if err := params.Validate(); err != nil {
|
if err := params.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -165,12 +214,12 @@ func GenerateLabel(w io.Writer, params *GenerateParameters, cfg *config.Config)
|
|||||||
qr.DisableBorder = true
|
qr.DisableBorder = true
|
||||||
qrImage := qr.Image(params.QrSize)
|
qrImage := qr.Image(params.QrSize)
|
||||||
|
|
||||||
regularFont, err := truetype.Parse(gomedium.TTF)
|
regularFont, err := loadFont(cfg, FontTypeRegular)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
boldFont, err := truetype.Parse(gobold.TTF)
|
boldFont, err := loadFont(cfg, FontTypeBold)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
187
backend/pkgs/labelmaker/labelmaker_test.go
Normal file
187
backend/pkgs/labelmaker/labelmaker_test.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package labelmaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||||
|
"golang.org/x/image/font/gofont/gobold"
|
||||||
|
"golang.org/x/image/font/gofont/gomedium"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadFont_WithNilConfig(t *testing.T) {
|
||||||
|
font, err := loadFont(nil, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
|
||||||
|
font, err = loadFont(nil, FontTypeBold)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_WithEmptyConfig(t *testing.T) {
|
||||||
|
cfg := &config.Config{}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
|
||||||
|
font, err = loadFont(cfg, FontTypeBold)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_WithCustomFontPath(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fontPath := filepath.Join(tempDir, "test-font.ttf")
|
||||||
|
|
||||||
|
err := os.WriteFile(fontPath, gomedium.TTF, 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cfg := &config.Config{
|
||||||
|
LabelMaker: config.LabelMakerConf{
|
||||||
|
RegularFontPath: &fontPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_WithNonExistentFontPath(t *testing.T) {
|
||||||
|
nonExistentPath := "/non/existent/path/font.ttf"
|
||||||
|
cfg := &config.Config{
|
||||||
|
LabelMaker: config.LabelMakerConf{
|
||||||
|
RegularFontPath: &nonExistentPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_UnknownFontType(t *testing.T) {
|
||||||
|
cfg := &config.Config{}
|
||||||
|
|
||||||
|
_, err := loadFont(cfg, FontType(999))
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unknown font type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_BoldFontWithCustomPath(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
fontPath := filepath.Join(tempDir, "test-bold-font.ttf")
|
||||||
|
|
||||||
|
err := os.WriteFile(fontPath, gobold.TTF, 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cfg := &config.Config{
|
||||||
|
LabelMaker: config.LabelMakerConf{
|
||||||
|
BoldFontPath: &fontPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeBold)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_EmptyStringPath(t *testing.T) {
|
||||||
|
emptyPath := ""
|
||||||
|
cfg := &config.Config{
|
||||||
|
LabelMaker: config.LabelMakerConf{
|
||||||
|
RegularFontPath: &emptyPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, font)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFont_CJKRendering(t *testing.T) {
|
||||||
|
cjkFontPath := filepath.Join("testdata", "NotoSansKR-VF.ttf")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
fontPath string
|
||||||
|
shouldHaveGlyph bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Korean with default font",
|
||||||
|
text: "한글",
|
||||||
|
fontPath: "",
|
||||||
|
shouldHaveGlyph: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chinese with default font",
|
||||||
|
text: "中文",
|
||||||
|
fontPath: "",
|
||||||
|
shouldHaveGlyph: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Japanese with default font",
|
||||||
|
text: "ひらがなカタカナ",
|
||||||
|
fontPath: "",
|
||||||
|
shouldHaveGlyph: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Korean with Noto Sans CJK",
|
||||||
|
text: "한글",
|
||||||
|
fontPath: cjkFontPath,
|
||||||
|
shouldHaveGlyph: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chinese with Noto Sans CJK",
|
||||||
|
text: "中文",
|
||||||
|
fontPath: cjkFontPath,
|
||||||
|
shouldHaveGlyph: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Japanese with Noto Sans CJK",
|
||||||
|
text: "ひらがなカタカナ",
|
||||||
|
fontPath: cjkFontPath,
|
||||||
|
shouldHaveGlyph: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var cfg *config.Config
|
||||||
|
if tt.fontPath != "" {
|
||||||
|
if _, err := os.Stat(tt.fontPath); os.IsNotExist(err) {
|
||||||
|
t.Skipf("Font file not found: %s", tt.fontPath)
|
||||||
|
}
|
||||||
|
cfg = &config.Config{
|
||||||
|
LabelMaker: config.LabelMakerConf{
|
||||||
|
RegularFontPath: &tt.fontPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
font, err := loadFont(cfg, FontTypeRegular)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, font)
|
||||||
|
|
||||||
|
hasAllGlyphs := true
|
||||||
|
for _, r := range tt.text {
|
||||||
|
if font.Index(r) == 0 {
|
||||||
|
hasAllGlyphs = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.shouldHaveGlyph {
|
||||||
|
assert.True(t, hasAllGlyphs, "Font should render %s characters", tt.name)
|
||||||
|
} else {
|
||||||
|
assert.False(t, hasAllGlyphs, "Default font should not render %s characters", tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
92
backend/pkgs/labelmaker/testdata/NotoSans-LICENSE
vendored
Normal file
92
backend/pkgs/labelmaker/testdata/NotoSans-LICENSE
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
This Font Software is licensed under the SIL Open Font License,
|
||||||
|
Version 1.1.
|
||||||
|
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font
|
||||||
|
creation efforts of academic and linguistic communities, and to
|
||||||
|
provide a free and open framework in which fonts may be shared and
|
||||||
|
improved in partnership with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply to
|
||||||
|
any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software
|
||||||
|
components as distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to,
|
||||||
|
deleting, or substituting -- in part or in whole -- any of the
|
||||||
|
components of the Original Version, by changing formats or by porting
|
||||||
|
the Font Software to a new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed,
|
||||||
|
modify, redistribute, and sell modified and unmodified copies of the
|
||||||
|
Font Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, in
|
||||||
|
Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the
|
||||||
|
corresponding Copyright Holder. This restriction only applies to the
|
||||||
|
primary font name as presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created using
|
||||||
|
the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
backend/pkgs/labelmaker/testdata/NotoSansKR-VF.ttf
vendored
Normal file
BIN
backend/pkgs/labelmaker/testdata/NotoSansKR-VF.ttf
vendored
Normal file
Binary file not shown.
15
backend/pkgs/labelmaker/testdata/README.md
vendored
Normal file
15
backend/pkgs/labelmaker/testdata/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Test Data
|
||||||
|
|
||||||
|
This directory contains font files used only for testing purposes.
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
- **NotoSansKR-VF.ttf**: Noto Sans CJK Korean Variable Font
|
||||||
|
- Used for testing CJK (Chinese, Japanese, Korean) character rendering
|
||||||
|
- License: See `NotoSans-LICENSE` file
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- These fonts are **only used during testing** and are **not included in production builds**
|
||||||
|
- Go's build system automatically excludes `testdata` directories from production builds
|
||||||
|
- The fonts support rendering of Korean, Chinese, and Japanese characters
|
||||||
@@ -48,6 +48,8 @@ aside: false
|
|||||||
| HBOX_LABEL_MAKER_PRINT_COMMAND | | the command to use for printing labels. if empty, label printing is disabled. <span v-pre>`{{.FileName}}`</span> in the command will be replaced with the png filename of the label |
|
| HBOX_LABEL_MAKER_PRINT_COMMAND | | the command to use for printing labels. if empty, label printing is disabled. <span v-pre>`{{.FileName}}`</span> in the command will be replaced with the png filename of the label |
|
||||||
| HBOX_LABEL_MAKER_DYNAMIC_LENGTH | true | allow label generation with open length. `HBOX_LABEL_MAKER_HEIGHT` is still used for layout and minimal height. If not used, long text may be cut off, but all labels have the same size. |
|
| HBOX_LABEL_MAKER_DYNAMIC_LENGTH | true | allow label generation with open length. `HBOX_LABEL_MAKER_HEIGHT` is still used for layout and minimal height. If not used, long text may be cut off, but all labels have the same size. |
|
||||||
| HBOX_LABEL_MAKER_ADDITIONAL_INFORMATION | | Additional information added to the label like name or phone number |
|
| HBOX_LABEL_MAKER_ADDITIONAL_INFORMATION | | Additional information added to the label like name or phone number |
|
||||||
|
| HBOX_LABEL_MAKER_REGULAR_FONT_PATH | | path to regular font file for label generation (e.g., `/fonts/NotoSansKR-Regular.ttf`). If not set, uses embedded font. Supports TTF format. |
|
||||||
|
| HBOX_LABEL_MAKER_BOLD_FONT_PATH | | path to bold font file for label generation (e.g., `/fonts/NotoSansKR-Bold.ttf`). If not set, uses embedded font. Supports TTF format. |
|
||||||
| HBOX_THUMBNAIL_ENABLED | true | enable thumbnail generation for images, supports PNG, JPEG, AVIF, WEBP, GIF file types |
|
| HBOX_THUMBNAIL_ENABLED | true | enable thumbnail generation for images, supports PNG, JPEG, AVIF, WEBP, GIF file types |
|
||||||
| HBOX_THUMBNAIL_WIDTH | 500 | width for generated thumbnails in pixels |
|
| HBOX_THUMBNAIL_WIDTH | 500 | width for generated thumbnails in pixels |
|
||||||
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
|
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
|
||||||
@@ -192,6 +194,8 @@ OPTIONS
|
|||||||
--label-maker-print-command/$HBOX_LABEL_MAKER_PRINT_COMMAND <string>
|
--label-maker-print-command/$HBOX_LABEL_MAKER_PRINT_COMMAND <string>
|
||||||
--label-maker-dynamic-length/$HBOX_LABEL_MAKER_DYNAMIC_LENGTH <bool> (default: true)
|
--label-maker-dynamic-length/$HBOX_LABEL_MAKER_DYNAMIC_LENGTH <bool> (default: true)
|
||||||
--label-maker-additional-information/$HBOX_LABEL_MAKER_ADDITIONAL_INFORMATION <string>
|
--label-maker-additional-information/$HBOX_LABEL_MAKER_ADDITIONAL_INFORMATION <string>
|
||||||
|
--label-maker-regular-font-path/$HBOX_LABEL_MAKER_REGULAR_FONT_PATH <string>
|
||||||
|
--label-maker-bold-font-path/$HBOX_LABEL_MAKER_BOLD_FONT_PATH <string>
|
||||||
--thumbnail-enabled/$HBOX_THUMBNAIL_ENABLED <bool> (default: true)
|
--thumbnail-enabled/$HBOX_THUMBNAIL_ENABLED <bool> (default: true)
|
||||||
--thumbnail-width/$HBOX_THUMBNAIL_WIDTH <int> (default: 500)
|
--thumbnail-width/$HBOX_THUMBNAIL_WIDTH <int> (default: 500)
|
||||||
--thumbnail-height/$HBOX_THUMBNAIL_HEIGHT <int> (default: 500)
|
--thumbnail-height/$HBOX_THUMBNAIL_HEIGHT <int> (default: 500)
|
||||||
|
|||||||
80
docs/en/custom-font-setup.md
Normal file
80
docs/en/custom-font-setup.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# External Font Support for Label Maker
|
||||||
|
|
||||||
|
Label maker supports external font files.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Docker/Podman Setup
|
||||||
|
|
||||||
|
1. **Download external fonts** (e.g., Noto Sans KR):
|
||||||
|
- Download from [Google Fonts](https://fonts.google.com/noto/specimen/Noto+Sans+KR)
|
||||||
|
- Or use the Variable Font from [GitHub](https://github.com/notofonts/noto-cjk)
|
||||||
|
|
||||||
|
2. **Create a fonts directory**:
|
||||||
|
```bash
|
||||||
|
mkdir -p ./fonts
|
||||||
|
# Place your font files in this directory
|
||||||
|
# e.g., NotoSansKR-VF.ttf
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Mount the fonts directory and set environment variables**:
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
homebox:
|
||||||
|
image: homebox:latest
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./fonts:/fonts:ro # Mount fonts directory as read-only
|
||||||
|
environment:
|
||||||
|
- HBOX_LABEL_MAKER_REGULAR_FONT_PATH=/fonts/NotoSansKR-VF.ttf
|
||||||
|
- HBOX_LABEL_MAKER_BOLD_FONT_PATH=/fonts/NotoSansKR-VF.ttf
|
||||||
|
ports:
|
||||||
|
- 3100:7745
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with podman:
|
||||||
|
```bash
|
||||||
|
podman run -d \
|
||||||
|
--name homebox \
|
||||||
|
-p 3100:7745 \
|
||||||
|
-v ./data:/data \
|
||||||
|
-v ./fonts:/fonts:ro \
|
||||||
|
-e HBOX_LABEL_MAKER_REGULAR_FONT_PATH=/fonts/NotoSansKR-VF.ttf \
|
||||||
|
-e HBOX_LABEL_MAKER_BOLD_FONT_PATH=/fonts/NotoSansKR-VF.ttf \
|
||||||
|
homebox:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Restart the container** and test label generation with Chinese, Japanese, Korean text!
|
||||||
|
|
||||||
|
## Supported Fonts
|
||||||
|
|
||||||
|
- **Format**: TTF (TrueType Font)
|
||||||
|
- **Recommended Fonts**:
|
||||||
|
- Noto Sans KR (Korean)
|
||||||
|
- Noto Sans CJK (Chinese, Japanese, Korean)
|
||||||
|
- Noto Sans SC (Simplified Chinese)
|
||||||
|
- Noto Sans JP (Japanese)
|
||||||
|
|
||||||
|
## Fallback Behavior
|
||||||
|
|
||||||
|
1. **External font specified** → Tries to load from `HBOX_LABEL_MAKER_*_FONT_PATH`
|
||||||
|
2. **External font fails or not specified** → Falls back to bundled Go fonts (Latin-only, **does not support CJK characters**)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Labels still show squares (□□□)
|
||||||
|
- Check if the font file exists at the specified path
|
||||||
|
- Verify the font file format (must be TTF, not OTF)
|
||||||
|
- Check container logs: `podman logs homebox | grep -i font`
|
||||||
|
|
||||||
|
### Font file not found
|
||||||
|
- Ensure the volume is correctly mounted
|
||||||
|
- Check file permissions (font files should be readable)
|
||||||
|
- Use absolute paths in environment variables
|
||||||
|
|
||||||
|
## Why External Fonts?
|
||||||
|
|
||||||
|
- **Smaller base image**: No need to embed large font files (~10MB per font)
|
||||||
|
- **Flexibility**: Easily switch fonts without rebuilding the image
|
||||||
|
- **Multi-language support**: Add support for any language by mounting appropriate fonts
|
||||||
Reference in New Issue
Block a user