Skip to content

Commit 8f425ee

Browse files
committed
EPUB optimizer and XTC/XTCH converter for Xteink X4 e-reader
Tools for 480x800 e-ink display (220 PPI, 128MB RAM): - EPUB sanitizer: removes complex CSS, fonts, optimizes images - EPUB to XTC/XTCH converter: renders to native binary format
0 parents  commit 8f425ee

18 files changed

+3638
-0
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
9+
.venv
10+
.pio
11+
.idea
12+
.DS_Store
13+
.vscode
14+
CLAUDE.md
15+
16+
# Font files (keep directory structure with .gitkeep)
17+
fonts/*
18+
!fonts/.gitkeep

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Dave Allie
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
.PHONY: help
2+
3+
#################################################################################
4+
# GLOBALS #
5+
#################################################################################
6+
7+
PROJECT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
8+
PROJECT_NAME = xteink-epub-optimizer
9+
10+
CURRENT_PATH = $(shell pwd)
11+
12+
VENV := $(or ${VIRTUAL_ENV},${VIRTUAL_ENV},.venv)
13+
PYTHON = $(VENV)/bin/python
14+
PIP = $(VENV)/bin/pip
15+
UV = $(VENV)/bin/uv
16+
17+
PYTHON_VERSION=3.12
18+
PYTHONPATH := $(or ${PYTHONPATH},.)
19+
TEST_DIR = tests/
20+
21+
LINT_SOURCES_PATHS = src/
22+
23+
export PYTHONPATH
24+
25+
#######################
26+
### System commands
27+
#######################
28+
.PHONY: tag
29+
## Create and push tag
30+
tag:
31+
@read -p "Enter tag version (e.g., 1.0.0): " TAG; \
32+
if [[ $$TAG =~ ^[0-9]+\.[0-9]+\.[0-9]+$$ ]]; then \
33+
git tag -a $$TAG -m $$TAG; \
34+
git push origin $$TAG; \
35+
echo "Tag $$TAG created and pushed successfully."; \
36+
else \
37+
echo "Invalid tag format. Please use X.Y.Z (e.g., 1.0.0)"; \
38+
exit 1; \
39+
fi
40+
#######################
41+
### Virtual environment
42+
#######################
43+
44+
.PHONY: venv/create
45+
venv/create: ## Create virtual environment
46+
@echo "create virtual environment..."
47+
python -m venv ${VENV}
48+
@echo "done"
49+
@echo
50+
51+
.PHONY: venv/install/main
52+
## Install main dependencies
53+
venv/install/main:
54+
@echo "install virtual environment..."
55+
$(UV) sync --no-group dev
56+
57+
.PHONY: venv/install/all
58+
## Install all dependencies
59+
venv/install/all:
60+
@echo "install virtual environment..."
61+
$(UV) sync --all-groups
62+
63+
########################################
64+
### EPUB Processing
65+
########################################
66+
67+
.PHONY: optimize
68+
## Optimize EPUBs for Xteink X4 (usage: make optimize INPUT=./input OUTPUT=./output)
69+
optimize:
70+
$(PYTHON) src/optimizer.py $(INPUT) $(OUTPUT)
71+
72+
# Default font path for conversion
73+
FONT ?= fonts/Bookerly.ttf
74+
FONT_BOLD ?= fonts/Bookerly Bold.ttf
75+
FONT_ITALIC ?= fonts/Bookerly Italic.ttf
76+
FONT_BOLD_ITALIC ?= fonts/Bookerly Bold Italic.ttf
77+
FONT_SIZE ?= 34
78+
79+
.PHONY: convert
80+
## Convert EPUB to XTC/XTCH (usage: make convert INPUT=book.epub OUTPUT=book.xtch FONT_SIZE=40)
81+
convert:
82+
$(PYTHON) src/converter.py $(INPUT) $(OUTPUT) \
83+
--font "$(FONT)" \
84+
--font-bold "$(FONT_BOLD)" \
85+
--font-italic "$(FONT_ITALIC)" \
86+
--font-bold-italic "$(FONT_BOLD_ITALIC)" \
87+
--font-size $(FONT_SIZE)
88+
89+
.PHONY: convert-mono
90+
## Convert EPUB to XTC (1-bit mono) (usage: make convert-mono INPUT=book.epub OUTPUT=book.xtc)
91+
convert-mono:
92+
$(PYTHON) src/converter.py $(INPUT) $(OUTPUT) --format xtc \
93+
--font "$(FONT)" \
94+
--font-bold "$(FONT_BOLD)" \
95+
--font-italic "$(FONT_ITALIC)" \
96+
--font-bold-italic "$(FONT_BOLD_ITALIC)" \
97+
--font-size $(FONT_SIZE)
98+
99+
########################################
100+
### Code style & formatting tools
101+
########################################
102+
103+
.PHONY: lint/black
104+
lint/black:
105+
@echo "linting using black..."
106+
$(PYTHON) -m black --check --diff $(LINT_SOURCES_PATHS)
107+
@echo "done"
108+
@echo
109+
110+
.PHONY: lint/flake8
111+
lint/flake8:
112+
@echo "linting using flake8..."
113+
$(PYTHON) -m flake8 $(LINT_SOURCES_PATHS)
114+
@echo "done"
115+
@echo
116+
117+
.PHONY: lint/isort
118+
lint/isort:
119+
@echo "linting using isort..."
120+
$(PYTHON) -m isort --check-only --diff $(LINT_SOURCES_PATHS)
121+
@echo "done"
122+
@echo
123+
124+
125+
.PHONY: lint
126+
## Running all linters
127+
lint: lint/black lint/flake8 lint/isort
128+
129+
.PHONY: format
130+
## Formatting source code
131+
format:
132+
@echo "formatting using black..."
133+
$(PYTHON) -m black $(LINT_SOURCES_PATHS)
134+
@echo "done"
135+
@echo "linting using isort..."
136+
$(PYTHON) -m isort $(LINT_SOURCES_PATHS)
137+
@echo "done"
138+
@echo
139+
140+
## Delete all compiled Python files
141+
clean: ## Clear temporary information
142+
@echo "Clear cache directories"
143+
rm -rf .mypy_cache .pytest_cache .coverage
144+
@rm -rf `find . -name __pycache__`
145+
@rm -rf `find . -type f -name '*.py[co]' `
146+
@rm -rf `find . -type f -name '*~' `
147+
@rm -rf `find . -type f -name '.*~' `
148+
@rm -rf `find . -type f -name '@*' `
149+
@rm -rf `find . -type f -name '#*#' `
150+
@rm -rf `find . -type f -name '*.orig' `
151+
@rm -rf `find . -type f -name '*.rej' `
152+
@rm -rf .coverage
153+
@rm -rf coverage.html
154+
@rm -rf coverage.xml
155+
@rm -rf htmlcov
156+
@rm -rf build
157+
@rm -rf cover
158+
@rm -rf .develop
159+
@rm -rf .flake
160+
@rm -rf .install-deps
161+
@rm -rf *.egg-info
162+
@rm -rf .pytest_cache
163+
@rm -rf .mypy_cache
164+
@rm -rf dist
165+
@rm -rf test-reports
166+
167+
####################
168+
### Tests
169+
####################
170+
171+
.PHONY: test
172+
## Run all tests
173+
test:
174+
PYTHONPATH=$(PYTHONPATH):src \
175+
$(PYTHON) -m pytest --disable-warnings $(TEST_DIR)
176+
177+
178+
#################################################################################
179+
# Self Documenting Commands #
180+
#################################################################################
181+
182+
.DEFAULT_GOAL := help
183+
184+
# Inspired by <http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html>
185+
# sed script explained:
186+
# /^##/:
187+
# * save line in hold space
188+
# * purge line
189+
# * Loop:
190+
# * append newline + line to hold space
191+
# * go to next line
192+
# * if line starts with doc comment, strip comment character off and loop
193+
# * remove target prerequisites
194+
# * append hold space (+ newline) to line
195+
# * replace newline plus comments by `---`
196+
# * print line
197+
# Separate expressions are necessary because labels cannot be delimited by
198+
# semicolon; see <http://stackoverflow.com/a/11799865/1968>
199+
.PHONY: help
200+
help:
201+
@echo "$$(tput bold)Available rules:$$(tput sgr0)"
202+
@echo
203+
@sed -n -e "/^## / { \
204+
h; \
205+
s/.*//; \
206+
:doc" \
207+
-e "H; \
208+
n; \
209+
s/^## //; \
210+
t doc" \
211+
-e "s/:.*//; \
212+
G; \
213+
s/\\n## /---/; \
214+
s/\\n/ /g; \
215+
p; \
216+
}" ${MAKEFILE_LIST} \
217+
| LC_ALL='C' sort --ignore-case \
218+
| awk -F '---' \
219+
-v ncol=$$(tput cols) \
220+
-v indent=19 \
221+
-v col_on="$$(tput setaf 6)" \
222+
-v col_off="$$(tput sgr0)" \
223+
'{ \
224+
printf "%s%*s%s ", col_on, -indent, $$1, col_off; \
225+
n = split($$2, words, " "); \
226+
line_length = ncol - indent; \
227+
for (i = 1; i <= n; i++) { \
228+
line_length -= length(words[i]) + 1; \
229+
if (line_length <= 0) { \
230+
line_length = ncol - indent - length(words[i]) - 1; \
231+
printf "\n%*s ", -indent, " "; \
232+
} \
233+
printf "%s ", words[i]; \
234+
} \
235+
printf "\n"; \
236+
}' \
237+
| more $(shell test $(shell uname) = Darwin && echo '--no-init --raw-control-chars')

0 commit comments

Comments
 (0)