tonhowtf/omniget · error

JPEG sem cabecalho SOF

Error message

JPEG sem cabecalho SOF

What it means

After confirming the SOI marker, jpeg_info walks the segment chain looking for a Start-Of-Frame marker (SOF0/SOF2, etc.) that carries width/height/channels. If it consumes the whole buffer without finding one, it raises "JPEG sem cabecalho SOF" (JPEG without SOF header). This happens for valid-signature but structurally broken or exotic files.

Solutions

  1. Verify the file opens in an image viewer / `file` reports a complete JPEG — if not, re-download it
  2. Compare bytes received against Content-Length to detect truncation before parsing
  3. Fall back to a robust decoder (image crate) to extract dimensions when the custom parser fails

Example fix

// before
let info = jpeg_info(&bytes)?; // Err: JPEG sem cabecalho SOF
// after
let info = match jpeg_info(&bytes) {
    Ok(i) => i,
    Err(_) => {
        let img = image::load_from_memory(&bytes)?; // robust fallback
        JpegInfo { width: img.width(), height: img.height(), components: 3 }
    }
};
Defensive patterns

Strategy: fallback

Validate before calling

// ensure the whole file arrived before parsing
assert_eq!(bytes.len(), expected_content_length as usize, "JPEG truncado");

Try / catch

let info = jpeg_info(&bytes).or_else(|_| {
    image::load_from_memory(&bytes).map(|img| JpegInfo {
        width: img.width(), height: img.height(), components: 3,
    })
})?;

Prevention

When it happens

Trigger: Truncated JPEG cut off before the SOF segment; progressive/EXIF files whose scan data precedes parsing assumptions and the loop skips past the SOF; concatenated/corrupted buffers where segment lengths lead parsing astray.

Common situations: Partially downloaded image (only header bytes); file corrupted in transfer/storage; JPEG produced by an unusual encoder that this simple parser does not handle.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/3c1bf6635e5fd27c. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/jpeg_pdf.rs:48

        if (0xD0..=0xD9).contains(&marker) || marker == 0x01 {
            i += 2;
            continue;
        }
        let len = u16::from_be_bytes([data[i + 2], data[i + 3]]) as usize;
        let is_sof = matches!(marker, 0xC0..=0xCF) && !matches!(marker, 0xC4 | 0xC8 | 0xCC);
        if is_sof {
            let height = u16::from_be_bytes([data[i + 5], data[i + 6]]) as u32;
            let width = u16::from_be_bytes([data[i + 7], data[i + 8]]) as u32;
            let components = data[i + 9];
            return Ok(JpegInfo {
                width,
                height,
                components,
            });
        }
        i += 2 + len;
    }
    Err(anyhow!("JPEG sem cabecalho SOF"))
}

pub fn is_jpeg(data: &[u8]) -> bool {
    data.len() > 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF
}

/// Monta o PDF em memória. Página no tamanho da imagem em pontos (72 dpi).
pub fn build_pdf(images: &[Vec<u8>]) -> anyhow::Result<Vec<u8>> {
    if images.is_empty() {
        return Err(anyhow!("nenhuma imagem"));
    }
    let mut out: Vec<u8> = Vec::new();
    let mut offsets: Vec<usize> = Vec::new();
    out.extend_from_slice(b"%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");

    // objetos: 1 catalog, 2 pages, depois por imagem: page, xobject, content
    let n_pages = images.len();
    let obj_page = |i: usize| 3 + i * 3;

View on GitHub (pinned to 8600b91f42)