@@ -5,6 +5,7 @@ use base64::Engine;
5
5
use image:: { DynamicImage , GenericImageView } ;
6
6
use serde:: de:: Error ;
7
7
use serde_derive:: { Deserialize , Serialize } ;
8
+ use svg2pdf:: usvg:: Image ;
8
9
9
10
use crate :: { ColorBits , ColorSpace } ;
10
11
@@ -1049,8 +1050,8 @@ pub(crate) fn image_to_stream(
1049
1050
) ,
1050
1051
] ) ;
1051
1052
1053
+ // Apply compression filter based on options
1052
1054
if let Some ( opts) = options {
1053
- // Set compression filter based on options
1054
1055
if let Some ( filter) = get_compression_filter ( opts, & rgb8) {
1055
1056
dict. set ( "Filter" , Name ( filter. into ( ) ) ) ;
1056
1057
@@ -1065,7 +1066,10 @@ pub(crate) fn image_to_stream(
1065
1066
}
1066
1067
1067
1068
if let Some ( alpha) = alpha {
1068
- let smask_dict = lopdf:: Dictionary :: from_iter ( vec ! [
1069
+
1070
+ use crate :: image:: ImageCompression :: * ;
1071
+
1072
+ let mut smask_dict = lopdf:: Dictionary :: from_iter ( vec ! [
1069
1073
( "Type" , Name ( "XObject" . into( ) ) ) ,
1070
1074
( "Subtype" , Name ( "Image" . into( ) ) ) ,
1071
1075
( "Width" , Integer ( rgb8. width as i64 ) ) ,
@@ -1075,16 +1079,56 @@ pub(crate) fn image_to_stream(
1075
1079
( "ColorSpace" , Name ( ColorSpace :: Greyscale . as_string( ) . into( ) ) ) ,
1076
1080
] ) ;
1077
1081
1078
- let mut stream = lopdf:: Stream :: new ( smask_dict, alpha. pixels ) . with_compression ( true ) ;
1082
+ let format = options. as_ref ( )
1083
+ . and_then ( |s| s. format )
1084
+ . unwrap_or_default ( ) ;
1085
+
1086
+ // Create alpha-specific options that prefer lossless compression
1087
+ let alpha_opts = ImageOptimizationOptions {
1088
+ // For alpha channel, we generally want to use lossless compression
1089
+ // unless specifically configured otherwise
1090
+ format : Some ( if matches ! ( format, Auto | Jpeg | Jpeg2000 ) {
1091
+ Flate // Use Flate for alpha by default
1092
+ } else {
1093
+ format // Otherwise use the same format as main image
1094
+ } ) ,
1095
+ .. options. cloned ( ) . unwrap_or_default ( )
1096
+ } ;
1097
+
1098
+ // Apply compression to alpha channel too, but prefer lossless methods for alpha
1099
+ if let Some ( filter) = get_compression_filter ( & alpha_opts, & alpha) {
1100
+ smask_dict. set ( "Filter" , Name ( filter. into ( ) ) ) ;
1101
+
1102
+ // Set DecodeParms for alpha channel if needed
1103
+ let jpeg_quality = options. as_ref ( ) . and_then ( |s| s. quality ) ;
1104
+ if matches ! ( filter, "DCTDecode" ) && jpeg_quality. is_some ( ) {
1105
+ let quality = ( jpeg_quality. unwrap ( ) * 100.0 ) as i64 ;
1106
+ smask_dict. set ( "DecodeParms" , Dictionary ( lopdf:: Dictionary :: from_iter ( vec ! [
1107
+ ( "Quality" , Integer ( quality) )
1108
+ ] ) ) ) ;
1109
+ }
1110
+ }
1079
1111
1080
- let _ = stream. compress ( ) ;
1112
+ let smask_has_filter = smask_dict. has ( b"Filter" ) ;
1113
+ let mut stream = lopdf:: Stream :: new ( smask_dict, alpha. pixels ) ;
1114
+
1115
+ // Only apply default compression if no filter was specified
1116
+ if !smask_has_filter {
1117
+ stream = stream. with_compression ( true ) ;
1118
+ let _ = stream. compress ( ) ;
1119
+ }
1081
1120
1082
1121
dict. set ( "SMask" , Reference ( doc. add_object ( stream) ) ) ;
1083
1122
}
1084
1123
1085
- let mut s = lopdf:: Stream :: new ( dict, rgb8. pixels ) . with_compression ( true ) ;
1086
-
1087
- let _ = s. compress ( ) ;
1124
+ let dict_has_filter = dict. has ( b"Filter" ) ;
1125
+ let mut s = lopdf:: Stream :: new ( dict, rgb8. pixels ) ;
1126
+
1127
+ // Only apply default compression if no filter was specified
1128
+ if !dict_has_filter {
1129
+ s = s. with_compression ( true ) ;
1130
+ let _ = s. compress ( ) ;
1131
+ }
1088
1132
1089
1133
s
1090
1134
}
0 commit comments