use boa_engine::Context;
use super::doc::SubmitRequest;
use super::model::{FieldModel, FieldModelKind};
fn exportable(field: &FieldModel) -> bool {
field.kind != FieldModelKind::Button && !field.flags.no_export
}
pub(super) fn serialize(request: &SubmitRequest, context: &mut Context) -> Vec<u8> {
let selected = select(request, context);
let fdf = write_fdf(&selected, &document_path(context));
if request.fdf {
return fdf;
}
url_encoded(&selected)
}
struct Submitted {
name: String,
value: String,
}
fn select(request: &SubmitRequest, context: &mut Context) -> Vec<Submitted> {
let Some(host) = super::bind::host(context) else {
return Vec::new();
};
let state = host.borrow();
let whole_form = request.fields.is_empty() && request.empty;
let mut out = Vec::new();
for field in &state.document.fields {
if !exportable(field) {
continue;
}
if !whole_form {
if !request.fields.contains(&field.name) {
continue;
}
if !request.empty && field.value.is_empty() {
continue;
}
}
if field.flags.required && field.value.is_empty() {
continue;
}
out.push(Submitted {
name: field.name.clone(),
value: field.value.clone(),
});
}
out
}
fn document_path(context: &Context) -> String {
super::bind::host(context)
.map(|host| host.borrow().document.url.clone())
.unwrap_or_default()
}
fn write_fdf(fields: &[Submitted], path: &str) -> Vec<u8> {
let mut out = String::from("%FDF-1.2\r\n1 0 obj\r\n<</FDF<<");
if !path.is_empty() {
out.push_str("/F<</F(");
out.push_str(&escape(path));
out.push_str(")/Type/Filespec/UF(");
out.push_str(&escape(path));
out.push_str(")>>");
}
out.push_str("/Fields[");
for field in fields {
out.push_str("<</T(");
out.push_str(&escape(&field.name));
out.push_str(")/V(");
out.push_str(&escape(&field.value));
out.push_str(")>>");
}
out.push_str("]>>>>\r\nendobj\r\n\r\ntrailer\r\n<</Root 1 0 R>>\r\n%%EOF\r\n");
out.into_bytes()
}
fn escape(text: &str) -> String {
let mut out = String::with_capacity(text.len());
for ch in text.chars() {
if matches!(ch, '(' | ')' | '\\') {
out.push('\\');
}
out.push(ch);
}
out
}
fn url_encoded(fields: &[Submitted]) -> Vec<u8> {
let mut out = String::new();
for (index, field) in fields.iter().enumerate() {
if index > 0 {
out.push('&');
}
out.push_str(&field.name);
out.push('=');
out.push_str(&field.value);
}
out.into_bytes()
}