Module: Pdfrb::Color::DefaultProfile
- Defined in:
- lib/pdfrb/color/default_profile.rb
Overview
Default sRGB ICC profile accessor. Builds a usable sRGB v4 ICC profile (header + tag table + tagged elements) that real PDF readers can consume for ICCBased color spaces. The profile includes:
* desc — profile description (sRGB by pdfrb)
* wtpt — D50 media white point
* rXYZ, gXYZ, bXYZ — sRGB colorant matrix (D50-adapted)
* rTRC, gTRC, bTRC — gamma 2.2 tone reproduction curves
Tagged-element types follow ICC.1:2022 §10 (multiLocalizedUnicodeType for desc, XYZType for colorants, curveType for TRCs).
Constant Summary collapse
- SRGB_SIGNATURE =
"acsp".b
- ICC_VERSION_V4 =
[0x04, 0x40, 0x00, 0x00].freeze
Class Method Summary collapse
-
.build_srgb_header(tag_section_size) ⇒ Object
---- Header construction ----.
-
.build_srgb_tags ⇒ Object
Returns a Hash of { tag_signature => tag_bytes } in the order they should appear after the tag table.
- .build_tag_table(tags) ⇒ Object
-
.curve_tag_bytes ⇒ Object
curveType with a single gamma value, per ICC.10.16.
- .d50_illuminant_bytes ⇒ Object
-
.d50_xyz ⇒ Object
---- sRGB colorants (D50-adapted, per ICC.1:2022 Annex A) ----.
- .date_bytes ⇒ Object
-
.desc_tag_bytes(text) ⇒ Object
multiLocalizedUnicodeType (mluc) for desc, per ICC.10.13.
-
.patch_profile_id(profile) ⇒ Object
Patch the Profile ID (offset 84..99) with the MD5 of the profile bytes computed with the ID slot zeroed (per ICC.1 §7.1.1).
-
.s15fixed16(value) ⇒ Object
Encode a Float as ICC s15Fixed16 (signed 16.16).
- .srgb_b_xyz ⇒ Object
-
.srgb_bytes ⇒ Object
Returns the sRGB ICC profile bytes (~ 600 bytes).
-
.srgb_color_space(document) ⇒ Object
Build an ICCBased color space array referencing a new stream containing the sRGB profile bytes.
- .srgb_g_xyz ⇒ Object
- .srgb_r_xyz ⇒ Object
- .tags_total_size(tags) ⇒ Object
-
.u8fixed8(value) ⇒ Object
Encode a Float as ICC u8Fixed8 (unsigned 8.8).
-
.xyz_tag_bytes(xyz_triplet) ⇒ Object
XYZType for colorants and white points, per ICC.10.22.
Class Method Details
.build_srgb_header(tag_section_size) ⇒ Object
---- Header construction ----
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pdfrb/color/default_profile.rb', line 55 def build_srgb_header(tag_section_size) header = +"" header << ([0] * 4).pack("C*") # Profile size placeholder header << ([0] * 4).pack("C*") # Preferred CMM header << ICC_VERSION_V4.pack("C*") # Version 4.4 header << "mntr" # Device class header << "RGB " # Color space header << "XYZ " # PCS header << date_bytes # Date header << SRGB_SIGNATURE # 'acsp' header << "APPL" # Primary platform header << ([0] * 4).pack("C*") # Profile flags header << ([0] * 4).pack("C*") # Device manufacturer header << ([0] * 4).pack("C*") # Device model header << ([0] * 8).pack("C*") # Device attributes header << ([0] * 4).pack("C*") # Rendering intent (perceptual) header << d50_illuminant_bytes # PCS illuminant (D50) header << ([0] * 4).pack("C*") # Profile creator header << ([0] * 16).pack("C*") # Profile ID (MD5, patched later) header << ([0] * 28).pack("C*") # Reserved total_size = header.bytesize + tag_section_size header[0, 4] = [total_size].pack("N") header end |
.build_srgb_tags ⇒ Object
Returns a Hash of { tag_signature => tag_bytes } in the order they should appear after the tag table.
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/pdfrb/color/default_profile.rb', line 96 def { "desc" => desc_tag_bytes("sRGB by pdfrb"), "wtpt" => xyz_tag_bytes(d50_xyz), "rXYZ" => xyz_tag_bytes(srgb_r_xyz), "gXYZ" => xyz_tag_bytes(srgb_g_xyz), "bXYZ" => xyz_tag_bytes(srgb_b_xyz), "rTRC" => curve_tag_bytes, "gTRC" => curve_tag_bytes, "bTRC" => curve_tag_bytes, } end |
.build_tag_table(tags) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/pdfrb/color/default_profile.rb', line 109 def build_tag_table() out = +"" count = .length out << [count].pack("N") # Compute offsets: tag table is 4 + count*12 bytes; each tag # element is appended after the table in order. offset = 4 + (count * 12) .each do |sig, bytes| out << sig out << [offset].pack("N") out << [bytes.bytesize].pack("N") offset += bytes.bytesize end out end |
.curve_tag_bytes ⇒ Object
curveType with a single gamma value, per ICC.10.16. 2.2 is the canonical sRGB approximation (real sRGB uses a piecewise curve, but a single 2.2 gamma is acceptable for most rendering).
160 161 162 163 164 165 166 167 |
# File 'lib/pdfrb/color/default_profile.rb', line 160 def curve_tag_bytes body = +"" body << "curv" # type signature body << ([0] * 4).pack("C*") # reserved body << [0].pack("N") # count = 0 → gamma follows in u8Fixed8 body << u8fixed8(2.2) body end |
.d50_illuminant_bytes ⇒ Object
86 87 88 89 90 |
# File 'lib/pdfrb/color/default_profile.rb', line 86 def d50_illuminant_bytes # D50 X=0.9642, Y=1.0, Z=0.8249 in s15Fixed16. [0x00, 0x00, 0xF6, 0xD6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D].pack("C*") end |
.d50_xyz ⇒ Object
---- sRGB colorants (D50-adapted, per ICC.1:2022 Annex A) ----
183 184 185 |
# File 'lib/pdfrb/color/default_profile.rb', line 183 def d50_xyz [0.9642, 1.0000, 0.8249] end |
.date_bytes ⇒ Object
81 82 83 84 |
# File 'lib/pdfrb/color/default_profile.rb', line 81 def date_bytes [0x07, 0xE8, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].pack("C*") end |
.desc_tag_bytes(text) ⇒ Object
multiLocalizedUnicodeType (mluc) for desc, per ICC.10.13.
133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/pdfrb/color/default_profile.rb', line 133 def desc_tag_bytes(text) utf16 = text.encode("UTF-16BE").bytes body = +"" body << "mluc" # type signature body << ([0] * 4).pack("C*") # reserved body << [1].pack("N") # number of records body << [12].pack("N") # record size in bytes body << "enUS" # language + country body << [utf16.length].pack("N") # string length in bytes body << [28].pack("N") # string offset (from tag start) body << utf16.pack("C*") body end |
.patch_profile_id(profile) ⇒ Object
Patch the Profile ID (offset 84..99) with the MD5 of the profile bytes computed with the ID slot zeroed (per ICC.1 §7.1.1).
204 205 206 207 208 209 210 |
# File 'lib/pdfrb/color/default_profile.rb', line 204 def patch_profile_id(profile) copy = profile.b copy[84, 16] = ([0] * 16).pack("C*") md5 = Digest::MD5.digest(copy) copy[84, 16] = md5 copy end |
.s15fixed16(value) ⇒ Object
Encode a Float as ICC s15Fixed16 (signed 16.16).
170 171 172 173 |
# File 'lib/pdfrb/color/default_profile.rb', line 170 def s15fixed16(value) v = (value * 65536.0).round.to_i [v].pack("N") end |
.srgb_b_xyz ⇒ Object
195 196 197 |
# File 'lib/pdfrb/color/default_profile.rb', line 195 def srgb_b_xyz [0.1431, 0.0606, 0.7141] end |
.srgb_bytes ⇒ Object
Returns the sRGB ICC profile bytes (~ 600 bytes). Includes the structural header + tag table + tagged elements needed for a real ICCBased color space.
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/pdfrb/color/default_profile.rb', line 28 def srgb_bytes = tag_table = build_tag_table() header = build_srgb_header(tag_table.bytesize + ()) profile = header + tag_table .each_value { |tag_bytes| profile << tag_bytes } # Compute MD5 ProfileID over the on-disk profile (sans the # existing ID slot, which we'll patch in). ICC.1:2022 §7.1. profile = patch_profile_id(profile) profile.force_encoding(Encoding::BINARY) end |
.srgb_color_space(document) ⇒ Object
Build an ICCBased color space array referencing a new stream containing the sRGB profile bytes. Returns [:ICCBased, ref] suitable for /ColorSpace resources.
43 44 45 46 47 48 49 50 51 |
# File 'lib/pdfrb/color/default_profile.rb', line 43 def srgb_color_space(document) bytes = srgb_bytes stream = document.add( { N: 3, Length: bytes.bytesize }, type: Pdfrb::Model::Cos::Stream ) stream.stream = bytes [:ICCBased, Pdfrb::Model::Reference.new(stream.oid, stream.gen)] end |
.srgb_g_xyz ⇒ Object
191 192 193 |
# File 'lib/pdfrb/color/default_profile.rb', line 191 def srgb_g_xyz [0.3851, 0.7169, 0.0971] end |
.srgb_r_xyz ⇒ Object
187 188 189 |
# File 'lib/pdfrb/color/default_profile.rb', line 187 def srgb_r_xyz [0.4360, 0.2225, 0.0139] end |
.tags_total_size(tags) ⇒ Object
126 127 128 |
# File 'lib/pdfrb/color/default_profile.rb', line 126 def () .values.sum(&:bytesize) end |
.u8fixed8(value) ⇒ Object
Encode a Float as ICC u8Fixed8 (unsigned 8.8).
176 177 178 179 |
# File 'lib/pdfrb/color/default_profile.rb', line 176 def u8fixed8(value) v = (value * 256.0).round.to_i [v].pack("n") end |
.xyz_tag_bytes(xyz_triplet) ⇒ Object
XYZType for colorants and white points, per ICC.10.22.
148 149 150 151 152 153 154 |
# File 'lib/pdfrb/color/default_profile.rb', line 148 def xyz_tag_bytes(xyz_triplet) body = +"" body << "XYZ " # type signature body << ([0] * 4).pack("C*") # reserved xyz_triplet.each { |v| body << s15fixed16(v) } body end |