Add cover page support with Chinese font rendering
Features:
- Cover page generation using Python/Pillow with Apple PingFang font
- CLI commands: cover-create, job-cover for cover page management
- API endpoints: PUT /api/fax/jobs/{id}/cover for cover modification
- Send fax with cover page: --to, --from, --subject, --note options
- Multi-page TIFF parsing for PDF conversion (fix: all pages now sent)
- Preview HTML generation with pan/zoom support
- OCR verification for cover page content
- Class 2 receive support for incoming faxes
Fixes:
- Modem initialization before Class 2 mode entry
- Dial timeout handling with retry logic
- Multi-page fax transmission between pages
- Chinese character rendering in cover pages (Apple native fonts)
This commit is contained in:
+61
-2
@@ -2,7 +2,7 @@ use axum::{
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
response::Json,
|
||||
routing::{get, post},
|
||||
routing::{get, post, put},
|
||||
Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -30,6 +30,10 @@ pub struct HealthResponse {
|
||||
pub struct SendFaxRequest {
|
||||
pub recipient: String,
|
||||
pub document_path: String,
|
||||
pub cover_to: Option<String>,
|
||||
pub cover_from: Option<String>,
|
||||
pub cover_subject: Option<String>,
|
||||
pub cover_notes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -37,6 +41,23 @@ pub struct SendFaxResponse {
|
||||
pub job_id: JobId,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdateCoverRequest {
|
||||
pub to: Option<String>,
|
||||
pub from: Option<String>,
|
||||
pub subject: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UpdateCoverResponse {
|
||||
pub job_id: JobId,
|
||||
pub cover_to: Option<String>,
|
||||
pub cover_from: Option<String>,
|
||||
pub cover_subject: Option<String>,
|
||||
pub cover_notes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct JobResponse {
|
||||
pub id: JobId,
|
||||
@@ -44,6 +65,10 @@ pub struct JobResponse {
|
||||
pub status: String,
|
||||
pub pages: u32,
|
||||
pub created_at: String,
|
||||
pub cover_to: Option<String>,
|
||||
pub cover_from: Option<String>,
|
||||
pub cover_subject: Option<String>,
|
||||
pub cover_notes: Option<String>,
|
||||
}
|
||||
|
||||
async fn health(State(state): State<AppState>) -> Json<HealthResponse> {
|
||||
@@ -57,7 +82,11 @@ async fn send_fax(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<SendFaxRequest>,
|
||||
) -> Result<Json<SendFaxResponse>, StatusCode> {
|
||||
let job = FaxJob::new(req.recipient, req.document_path);
|
||||
let mut job = FaxJob::new(req.recipient, req.document_path);
|
||||
job.cover_to = req.cover_to;
|
||||
job.cover_from = req.cover_from;
|
||||
job.cover_subject = req.cover_subject;
|
||||
job.cover_notes = req.cover_notes;
|
||||
let job_id = job.id;
|
||||
|
||||
let mut queue = state.queue.lock().unwrap();
|
||||
@@ -78,6 +107,10 @@ async fn list_jobs(State(state): State<AppState>) -> Json<Vec<JobResponse>> {
|
||||
status: format!("{:?}", j.status),
|
||||
pages: j.pages,
|
||||
created_at: j.created_at.to_rfc3339(),
|
||||
cover_to: j.cover_to,
|
||||
cover_from: j.cover_from,
|
||||
cover_subject: j.cover_subject,
|
||||
cover_notes: j.cover_notes,
|
||||
})
|
||||
.collect();
|
||||
Json(responses)
|
||||
@@ -94,9 +127,34 @@ async fn get_job(
|
||||
status: format!("{:?}", j.status),
|
||||
pages: j.pages,
|
||||
created_at: j.created_at.to_rfc3339(),
|
||||
cover_to: j.cover_to,
|
||||
cover_from: j.cover_from,
|
||||
cover_subject: j.cover_subject,
|
||||
cover_notes: j.cover_notes,
|
||||
})).ok_or(StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
async fn update_job_cover(
|
||||
State(state): State<AppState>,
|
||||
axum::extract::Path(id): axum::extract::Path<JobId>,
|
||||
Json(req): Json<UpdateCoverRequest>,
|
||||
) -> Result<Json<UpdateCoverResponse>, StatusCode> {
|
||||
let mut queue = state.queue.lock().unwrap();
|
||||
queue
|
||||
.update_cover(&id, req.to.clone(), req.from.clone(), req.subject.clone(), req.notes.clone())
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
// Return updated fields
|
||||
let job = queue.get(&id).ok_or(StatusCode::NOT_FOUND)?;
|
||||
Ok(Json(UpdateCoverResponse {
|
||||
job_id: job.id,
|
||||
cover_to: job.cover_to,
|
||||
cover_from: job.cover_from,
|
||||
cover_subject: job.cover_subject,
|
||||
cover_notes: job.cover_notes,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn start_server(config: FaxConfig, queue: Arc<Mutex<FaxQueue>>) -> FaxResult<()> {
|
||||
let state = AppState {
|
||||
config: config.clone(),
|
||||
@@ -108,6 +166,7 @@ pub async fn start_server(config: FaxConfig, queue: Arc<Mutex<FaxQueue>>) -> Fax
|
||||
.route("/api/fax/send", post(send_fax))
|
||||
.route("/api/fax/jobs", get(list_jobs))
|
||||
.route("/api/fax/jobs/{id}", get(get_job))
|
||||
.route("/api/fax/jobs/{id}/cover", put(update_job_cover))
|
||||
.with_state(state);
|
||||
|
||||
let addr = config
|
||||
|
||||
Reference in New Issue
Block a user