roxify 1.9.4 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "roxify_native"
3
- version = "1.9.4"
3
+ version = "1.9.6"
4
4
  edition = "2021"
5
5
  publish = false
6
6
 
@@ -126,6 +126,13 @@ export async function listFilesInPng(pngBuf, opts = {}) {
126
126
  }
127
127
  }
128
128
  catch (e) { }
129
+ try {
130
+ const json = native.extractFileListFromPixels(pngBuf);
131
+ if (json) {
132
+ return parseFileList(JSON.parse(json));
133
+ }
134
+ }
135
+ catch (e) { }
129
136
  return null;
130
137
  }
131
138
  export async function hasPassphraseInPng(pngBuf) {
@@ -1,14 +1,21 @@
1
- use image::{ImageFormat, DynamicImage};
1
+ use image::{ImageFormat, DynamicImage, ImageReader};
2
2
  use std::io::Cursor;
3
3
 
4
+ fn load_no_limits(input: &[u8]) -> Result<DynamicImage, String> {
5
+ let mut reader = ImageReader::new(Cursor::new(input))
6
+ .with_guessed_format()
7
+ .map_err(|e| format!("Failed to guess format: {}", e))?;
8
+ reader.no_limits();
9
+ reader.decode().map_err(|e| format!("Failed to load image: {}", e))
10
+ }
11
+
4
12
  pub fn sharp_resize(
5
13
  input: &[u8],
6
14
  width: u32,
7
15
  height: u32,
8
16
  kernel: &str,
9
17
  ) -> Result<Vec<u8>, String> {
10
- let img = image::load_from_memory(input)
11
- .map_err(|e| format!("Failed to load image: {}", e))?;
18
+ let img = load_no_limits(input)?;
12
19
 
13
20
  let filter = match kernel {
14
21
  "nearest" => image::imageops::FilterType::Nearest,
@@ -28,8 +35,7 @@ pub fn sharp_resize(
28
35
  }
29
36
 
30
37
  pub fn sharp_raw_pixels(input: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
31
- let img = image::load_from_memory(input)
32
- .map_err(|e| format!("Failed to load image: {}", e))?;
38
+ let img = load_no_limits(input)?;
33
39
 
34
40
  let rgb = img.to_rgb8();
35
41
  let width = rgb.width();
@@ -40,8 +46,7 @@ pub fn sharp_raw_pixels(input: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
40
46
  }
41
47
 
42
48
  pub fn sharp_metadata(input: &[u8]) -> Result<(u32, u32, String), String> {
43
- let img = image::load_from_memory(input)
44
- .map_err(|e| format!("Failed to load image: {}", e))?;
49
+ let img = load_no_limits(input)?;
45
50
 
46
51
  let width = img.width();
47
52
  let height = img.height();
package/native/lib.rs CHANGED
@@ -389,6 +389,13 @@ pub fn crop_and_reconstitute(png_buffer: Buffer) -> Result<Vec<u8>> {
389
389
  .map_err(|e| Error::from_reason(e))
390
390
  }
391
391
 
392
+ #[cfg(not(test))]
393
+ #[napi]
394
+ pub fn extract_file_list_from_pixels(png_buffer: Buffer) -> Result<String> {
395
+ png_utils::extract_file_list_from_pixels(&png_buffer)
396
+ .map_err(|e| Error::from_reason(e))
397
+ }
398
+
392
399
  // ─── WAV container NAPI exports ──────────────────────────────────────────────
393
400
 
394
401
  #[cfg(not(test))]
@@ -1,5 +1,6 @@
1
1
  use bytemuck::{Pod, Zeroable};
2
- use std::io::{Read, Seek, SeekFrom};
2
+ use image::ImageReader;
3
+ use std::io::{Cursor, Read, Seek, SeekFrom};
3
4
 
4
5
  #[repr(C)]
5
6
  #[derive(Clone, Copy, Pod, Zeroable)]
@@ -176,7 +177,11 @@ fn find_pixel_header(raw: &[u8]) -> Result<usize, String> {
176
177
  }
177
178
 
178
179
  fn decode_to_rgb(png_data: &[u8]) -> Result<Vec<u8>, String> {
179
- let img = image::load_from_memory(png_data).map_err(|e| format!("image load error: {}", e))?;
180
+ let mut reader = ImageReader::new(Cursor::new(png_data))
181
+ .with_guessed_format()
182
+ .map_err(|e| format!("format guess error: {}", e))?;
183
+ reader.no_limits();
184
+ let img = reader.decode().map_err(|e| format!("image decode error: {}", e))?;
180
185
  Ok(img.to_rgb8().into_raw())
181
186
  }
182
187
 
@@ -184,8 +189,13 @@ pub fn extract_name_from_png(png_data: &[u8]) -> Option<String> {
184
189
  if let Some(name) = extract_name_direct(png_data) {
185
190
  return Some(name);
186
191
  }
187
- let reconst = crate::reconstitution::crop_and_reconstitute(png_data).ok()?;
188
- extract_name_direct(&reconst)
192
+ if let Ok(reconst) = crate::reconstitution::crop_and_reconstitute(png_data) {
193
+ if let Some(name) = extract_name_direct(&reconst) {
194
+ return Some(name);
195
+ }
196
+ }
197
+ let unstretched = crate::reconstitution::unstretch_nn(png_data).ok()?;
198
+ extract_name_direct(&unstretched)
189
199
  }
190
200
 
191
201
  fn extract_name_direct(png_data: &[u8]) -> Option<String> {
@@ -224,8 +234,13 @@ pub fn extract_file_list_from_pixels(png_data: &[u8]) -> Result<String, String>
224
234
  if let Ok(result) = extract_file_list_direct(png_data) {
225
235
  return Ok(result);
226
236
  }
227
- let reconst = crate::reconstitution::crop_and_reconstitute(png_data)?;
228
- extract_file_list_direct(&reconst)
237
+ if let Ok(reconst) = crate::reconstitution::crop_and_reconstitute(png_data) {
238
+ if let Ok(result) = extract_file_list_direct(&reconst) {
239
+ return Ok(result);
240
+ }
241
+ }
242
+ let unstretched = crate::reconstitution::unstretch_nn(png_data)?;
243
+ extract_file_list_direct(&unstretched)
229
244
  }
230
245
 
231
246
  fn extract_file_list_direct(png_data: &[u8]) -> Result<String, String> {
@@ -1,5 +1,14 @@
1
- use image::{Rgba, RgbaImage};
1
+ use image::{Rgba, RgbaImage, DynamicImage, ImageReader};
2
2
  use std::cmp::min;
3
+ use std::io::Cursor;
4
+
5
+ fn load_image_no_limits(data: &[u8]) -> Result<DynamicImage, String> {
6
+ let mut reader = ImageReader::new(Cursor::new(data))
7
+ .with_guessed_format()
8
+ .map_err(|e| format!("format guess error: {}", e))?;
9
+ reader.no_limits();
10
+ reader.decode().map_err(|e| format!("image decode error: {}", e))
11
+ }
3
12
 
4
13
  fn color_dist(a: [u8; 4], b: [u8; 4]) -> i32 {
5
14
  (a[0] as i32 - b[0] as i32).abs() +
@@ -77,7 +86,7 @@ fn intra_block_transitions_v(
77
86
  }
78
87
 
79
88
  pub fn crop_and_reconstitute(png_data: &[u8]) -> Result<Vec<u8>, String> {
80
- let img = image::load_from_memory(png_data).unwrap();
89
+ let img = load_image_no_limits(png_data)?;
81
90
  let rgba = img.to_rgba8();
82
91
  let width = rgba.width();
83
92
  let height = rgba.height();
@@ -411,13 +420,13 @@ pub fn crop_and_reconstitute(png_data: &[u8]) -> Result<Vec<u8>, String> {
411
420
  }
412
421
 
413
422
  let mut output = Vec::new();
414
- out.write_to(&mut std::io::Cursor::new(&mut output), image::ImageFormat::Png).unwrap();
423
+ out.write_to(&mut std::io::Cursor::new(&mut output), image::ImageFormat::Png)
424
+ .map_err(|e| format!("PNG write error: {}", e))?;
415
425
  Ok(output)
416
426
  }
417
427
 
418
428
  pub fn unstretch_nn(png_data: &[u8]) -> Result<Vec<u8>, String> {
419
- let img = image::load_from_memory(png_data)
420
- .map_err(|e| format!("image load error: {}", e))?;
429
+ let img = load_image_no_limits(png_data)?;
421
430
  let rgba = img.to_rgba8();
422
431
  let width = rgba.width() as usize;
423
432
  let height = rgba.height() as usize;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roxify",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "type": "module",
5
5
  "description": "Ultra-lightweight PNG steganography with native Rust acceleration. Encode binary data into PNG images with zstd compression.",
6
6
  "main": "dist/index.js",