Mayo commited on
Commit
e63ac18
·
unverified ·
1 Parent(s): 3c20efb

fix: PSD text placement & editable

Browse files
koharu-psd/src/export.rs CHANGED
@@ -38,7 +38,7 @@ impl Default for PsdExportOptions {
38
  include_inpainted: true,
39
  include_segment_mask: true,
40
  include_brush_layer: true,
41
- text_layer_mode: TextLayerMode::Rasterized,
42
  }
43
  }
44
  }
@@ -730,15 +730,20 @@ fn is_probably_latin(text: &str) -> bool {
730
 
731
  #[cfg(test)]
732
  mod tests {
 
 
 
733
  use image::{Rgba, RgbaImage};
734
 
735
  use crate::writer::PsdWriter;
736
 
737
- use crate::input::{PsdTextBlock, PsdTextDirection, PsdTextStyle};
 
 
738
 
739
  use super::{
740
- TextOrientation, contains_cjk, infer_font_name, infer_orientation, is_probably_latin,
741
- place_on_canvas, write_image_data,
742
  };
743
 
744
  #[test]
@@ -815,4 +820,44 @@ mod tests {
815
 
816
  assert_eq!(infer_font_name(&block), "ArialMT");
817
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  }
 
38
  include_inpainted: true,
39
  include_segment_mask: true,
40
  include_brush_layer: true,
41
+ text_layer_mode: TextLayerMode::Editable,
42
  }
43
  }
44
  }
 
730
 
731
  #[cfg(test)]
732
  mod tests {
733
+ use std::collections::HashMap;
734
+
735
+ use image::DynamicImage;
736
  use image::{Rgba, RgbaImage};
737
 
738
  use crate::writer::PsdWriter;
739
 
740
+ use crate::input::{
741
+ PsdDocument, PsdTextBlock, PsdTextDirection, PsdTextStyle, ResolvedDocument,
742
+ };
743
 
744
  use super::{
745
+ PsdExportOptions, TextLayerMode, TextOrientation, contains_cjk, export_document,
746
+ infer_font_name, infer_orientation, is_probably_latin, place_on_canvas, write_image_data,
747
  };
748
 
749
  #[test]
 
820
 
821
  assert_eq!(infer_font_name(&block), "ArialMT");
822
  }
823
+
824
+ #[test]
825
+ fn default_export_writes_editable_text_layer_metadata() {
826
+ let source =
827
+ DynamicImage::ImageRgba8(RgbaImage::from_pixel(16, 16, Rgba([255, 255, 255, 255])));
828
+ let document = PsdDocument {
829
+ width: 16,
830
+ height: 16,
831
+ text_blocks: vec![PsdTextBlock {
832
+ x: 2.0,
833
+ y: 3.0,
834
+ width: 10.0,
835
+ height: 8.0,
836
+ translation: Some("Hello".to_string()),
837
+ ..Default::default()
838
+ }],
839
+ };
840
+ let block_images = HashMap::new();
841
+ let resolved = ResolvedDocument {
842
+ document: &document,
843
+ source: &source,
844
+ segment: None,
845
+ inpainted: None,
846
+ rendered: None,
847
+ brush_layer: None,
848
+ block_images: &block_images,
849
+ };
850
+
851
+ let options = PsdExportOptions {
852
+ include_original: false,
853
+ ..Default::default()
854
+ };
855
+ assert_eq!(options.text_layer_mode, TextLayerMode::Editable);
856
+
857
+ let bytes = export_document(&resolved, &options).expect("export PSD");
858
+ assert!(
859
+ bytes.windows(4).any(|window| window == b"TySh"),
860
+ "editable PSD export should include text layer metadata",
861
+ );
862
+ }
863
  }
koharu-rpc/src/psd_export.rs CHANGED
@@ -157,12 +157,17 @@ fn text_to_psd(
157
  transform: &koharu_core::Transform,
158
  text: &TextData,
159
  ) -> PsdTextBlock {
 
 
 
 
 
160
  PsdTextBlock {
161
  id: node_id.to_string(),
162
- x: transform.x,
163
- y: transform.y,
164
- width: transform.width,
165
- height: transform.height,
166
  translation: text.translation.clone(),
167
  style: text.style.as_ref().map(convert_style),
168
  rendered: text.sprite.as_ref().map(blob_ref_to_psd),
@@ -220,3 +225,70 @@ fn convert_prediction(p: &FontPrediction) -> PsdFontPrediction {
220
  fn blob_ref_to_psd(r: &BlobRef) -> PsdBlobRef {
221
  PsdBlobRef::new(r.hash())
222
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  transform: &koharu_core::Transform,
158
  text: &TextData,
159
  ) -> PsdTextBlock {
160
+ let layer_transform = match (&text.sprite, &text.sprite_transform) {
161
+ (Some(_), Some(sprite_transform)) => sprite_transform,
162
+ _ => transform,
163
+ };
164
+
165
  PsdTextBlock {
166
  id: node_id.to_string(),
167
+ x: layer_transform.x,
168
+ y: layer_transform.y,
169
+ width: layer_transform.width,
170
+ height: layer_transform.height,
171
  translation: text.translation.clone(),
172
  style: text.style.as_ref().map(convert_style),
173
  rendered: text.sprite.as_ref().map(blob_ref_to_psd),
 
225
  fn blob_ref_to_psd(r: &BlobRef) -> PsdBlobRef {
226
  PsdBlobRef::new(r.hash())
227
  }
228
+
229
+ #[cfg(test)]
230
+ mod tests {
231
+ use koharu_core::{BlobRef, NodeId, TextData, Transform};
232
+
233
+ use super::text_to_psd;
234
+
235
+ #[test]
236
+ fn text_to_psd_uses_sprite_transform_for_rendered_text_layer() {
237
+ let node_transform = Transform {
238
+ x: 10.0,
239
+ y: 20.0,
240
+ width: 30.0,
241
+ height: 40.0,
242
+ rotation_deg: 0.0,
243
+ };
244
+ let sprite_transform = Transform {
245
+ x: 100.0,
246
+ y: 200.0,
247
+ width: 300.0,
248
+ height: 400.0,
249
+ rotation_deg: 0.0,
250
+ };
251
+ let text = TextData {
252
+ translation: Some("Hello".to_string()),
253
+ sprite: Some(BlobRef::new("sprite")),
254
+ sprite_transform: Some(sprite_transform),
255
+ ..Default::default()
256
+ };
257
+
258
+ let block = text_to_psd(&NodeId::new(), &node_transform, &text);
259
+
260
+ assert_eq!(block.x, sprite_transform.x);
261
+ assert_eq!(block.y, sprite_transform.y);
262
+ assert_eq!(block.width, sprite_transform.width);
263
+ assert_eq!(block.height, sprite_transform.height);
264
+ }
265
+
266
+ #[test]
267
+ fn text_to_psd_ignores_stale_sprite_transform_without_sprite() {
268
+ let node_transform = Transform {
269
+ x: 10.0,
270
+ y: 20.0,
271
+ width: 30.0,
272
+ height: 40.0,
273
+ rotation_deg: 0.0,
274
+ };
275
+ let text = TextData {
276
+ translation: Some("Hello".to_string()),
277
+ sprite_transform: Some(Transform {
278
+ x: 100.0,
279
+ y: 200.0,
280
+ width: 300.0,
281
+ height: 400.0,
282
+ rotation_deg: 0.0,
283
+ }),
284
+ ..Default::default()
285
+ };
286
+
287
+ let block = text_to_psd(&NodeId::new(), &node_transform, &text);
288
+
289
+ assert_eq!(block.x, node_transform.x);
290
+ assert_eq!(block.y, node_transform.y);
291
+ assert_eq!(block.width, node_transform.width);
292
+ assert_eq!(block.height, node_transform.height);
293
+ }
294
+ }