From 0f572539d27cd5725d5300121e9f5fe652b44e71 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Fri, 24 Jul 2026 23:52:08 +0800 Subject: [PATCH] Fix EMRTEXT wire layout: ptlReference is POINTL, not WmfRect16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After studying libemf2svg's emf2svg_rec_drawing.c and the U_EMRTEXT struct in libUEMF, discovered the wire layout was wrong. Previous (incorrect): int16 wmf_rect_left/top/right/bottom (WmfRect16, 8 bytes) Correct (per MS-EMF 2.2.6 EMRTEXT): point_l ptl_reference (POINTL, 8 bytes — int32 x, int32 y) uint32 n_chars uint32 off_string uint32 f_options rectl rcl (RECTL, 16 bytes — clipping rectangle) uint32 off_dx The ptlReference is the text anchor point — the most critical field for text positioning. Was previously parsed as 4 int16 values (wmf_rect_left/top/right/bottom) which mapped to the wrong bytes, causing all text to be positioned at incorrect coordinates. Round-trip verified: 208/208 byte-identical. --- lib/emf/emr/binary/records/ext_text_out_a.rb | 15 ++++--------- lib/emf/emr/binary/records/ext_text_out_w.rb | 23 +++++++++----------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/lib/emf/emr/binary/records/ext_text_out_a.rb b/lib/emf/emr/binary/records/ext_text_out_a.rb index 1aa0077..dbab767 100644 --- a/lib/emf/emr/binary/records/ext_text_out_a.rb +++ b/lib/emf/emr/binary/records/ext_text_out_a.rb @@ -7,24 +7,17 @@ module Emf module Emr module Binary module Records - # EMR_EXTTEXTOUTA per MS-EMF 2.3.8.1. Same layout as ExtTextOutW - # except the string is 8-bit ANSI (codepage-dependent) rather - # than UTF-16LE. + # EMR_EXTTEXTOUTA per MS-EMF 2.3.8.1. Same EMRTEXT layout as + # ExtTextOutW except the string is 8-bit ANSI. class ExtTextOutA < Emf::Emr::Binary::WithBounds uint32 :i_graphics_mode float :ex_scale float :ey_scale - int16 :wmf_rect_left - int16 :wmf_rect_top - int16 :wmf_rect_right - int16 :wmf_rect_bottom + point_l :ptl_reference uint32 :n_chars uint32 :off_string uint32 :f_options - int16 :wmf_rect2_left - int16 :wmf_rect2_top - int16 :wmf_rect2_right - int16 :wmf_rect2_bottom + rectl :rcl uint32 :off_dx rest :trailing end diff --git a/lib/emf/emr/binary/records/ext_text_out_w.rb b/lib/emf/emr/binary/records/ext_text_out_w.rb index 7233f8f..93ee2b5 100644 --- a/lib/emf/emr/binary/records/ext_text_out_w.rb +++ b/lib/emf/emr/binary/records/ext_text_out_w.rb @@ -7,34 +7,31 @@ module Emf module Emr module Binary module Records - # EMR_EXTTEXTOUTW per MS-EMF 2.3.8.2. The layout after the header: - # rclBounds (RECTL, in WithBounds) + # EMR_EXTTEXTOUTW per MS-EMF 2.3.8.2. The EMRTEXT struct follows: + # emr (8 bytes, in WithBounds header) + # rclBounds (16 bytes, in WithBounds) # iGraphicsMode (uint32) # exScale (float) # eyScale (float) - # ref_WmfRect16 (RECT_S: 4 int16 = 8 bytes) + # --- EMRTEXT starts --- + # ptlReference (POINTL: int32 x, int32 y) — text anchor point # nChars (uint32) - # offString (uint32) — byte offset from record start to string + # offString (uint32) — byte offset from record start # fOptions (uint32) — ExtTextOutOptions flags - # ref_WmfRect16_2 (RECT_S) + # rcl (RECTL) — clipping rectangle # offDx (uint32) — byte offset to Dx array + # --- EMRTEXT ends --- # ...string (UTF-16LE, nChars chars)... # ...Dx array (uint32 per char)... class ExtTextOutW < Emf::Emr::Binary::WithBounds uint32 :i_graphics_mode float :ex_scale float :ey_scale - int16 :wmf_rect_left - int16 :wmf_rect_top - int16 :wmf_rect_right - int16 :wmf_rect_bottom + point_l :ptl_reference uint32 :n_chars uint32 :off_string uint32 :f_options - int16 :wmf_rect2_left - int16 :wmf_rect2_top - int16 :wmf_rect2_right - int16 :wmf_rect2_bottom + rectl :rcl uint32 :off_dx rest :trailing end