Skip to content

Commit ed446fd

Browse files
committed
Fix PDF annotation coordinate transformation
- PDF text() expects DATA coordinates but was receiving pixel coordinates - Added backend-aware coordinate handling in render_figure_annotations - COORD_FIGURE and COORD_AXIS now properly converted to data space for PDF - Fixes garbled/mispositioned text in annotation_demo.pdf - Addresses annotation positioning issues in #1413
1 parent 7318471 commit ed446fd

File tree

1 file changed

+52
-18
lines changed

1 file changed

+52
-18
lines changed

src/text/fortplot_annotation_rendering.f90

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,39 @@ subroutine render_figure_annotations(backend, annotations, annotation_count, &
2727
margin_left, margin_right, &
2828
margin_bottom, margin_top)
2929
!! Render all annotations for the current figure
30-
!!
30+
!!
3131
!! This is the main entry point called from figure_render() that processes
3232
!! all stored annotations and dispatches them to the appropriate backend.
3333
!! Uses existing backend text rendering infrastructure.
34-
34+
35+
use fortplot_pdf, only: pdf_context
36+
3537
class(plot_context), intent(inout) :: backend
3638
type(text_annotation_t), intent(in) :: annotations(:)
3739
integer, intent(in) :: annotation_count
3840
real(wp), intent(in) :: x_min, x_max, y_min, y_max
3941
integer, intent(in) :: width, height
4042
real(wp), intent(in) :: margin_left, margin_right, margin_bottom, margin_top
41-
43+
4244
integer :: i
4345
real(wp) :: render_x, render_y
44-
logical :: valid_annotation
46+
logical :: valid_annotation, is_pdf_backend
4547
character(len=256) :: error_message
46-
48+
4749
! Early exit if no annotations
4850
if (annotation_count == 0) return
49-
51+
52+
! Check if backend is PDF (PDF expects data coordinates, not pixels)
53+
select type (backend)
54+
type is (pdf_context)
55+
is_pdf_backend = .true.
56+
class default
57+
is_pdf_backend = .false.
58+
end select
59+
5060
call log_info("Rendering annotations: processing " // &
5161
trim(adjustl(int_to_char(annotation_count))) // " annotations")
52-
62+
5363
! Process each annotation
5464
do i = 1, annotation_count
5565
! Skip re-validation if already validated at creation time (Issue #870: prevent duplicate warnings)
@@ -68,21 +78,45 @@ subroutine render_figure_annotations(backend, annotations, annotation_count, &
6878
cycle
6979
end if
7080
end if
71-
81+
7282
! Skip pie chart label/autopct annotations for ASCII and PDF backends
7383
! ASCII backend uses legend-only approach for cleaner output
7484
! PDF backend has coordinate transformation issues with pie annotations
7585
if (should_skip_pie_annotation(backend, annotations(i))) then
7686
cycle
7787
end if
78-
79-
! Transform coordinates to rendering coordinates
80-
call transform_annotation_to_rendering_coords(annotations(i), &
81-
x_min, x_max, y_min, y_max, &
82-
width, height, &
83-
margin_left, margin_right, &
84-
margin_bottom, margin_top, &
85-
render_x, render_y)
88+
89+
! PDF backend text() expects DATA coordinates and applies normalize_to_pdf_coords
90+
! But annotations can be in FIGURE or AXIS coordinates, so we need special handling
91+
if (is_pdf_backend .and. annotations(i)%coord_type /= COORD_DATA) then
92+
! For PDF with FIGURE/AXIS coordinates: convert to DATA coordinates first
93+
! Then PDF's text() will apply normalize_to_pdf_coords
94+
select case (annotations(i)%coord_type)
95+
case (COORD_FIGURE)
96+
! Figure coordinates (0-1): map to data space
97+
render_x = x_min + annotations(i)%x * (x_max - x_min)
98+
render_y = y_min + annotations(i)%y * (y_max - y_min)
99+
case (COORD_AXIS)
100+
! Axis coordinates (0-1 in plot area): map to data space
101+
render_x = x_min + annotations(i)%x * (x_max - x_min)
102+
render_y = y_min + annotations(i)%y * (y_max - y_min)
103+
case default
104+
render_x = annotations(i)%x
105+
render_y = annotations(i)%y
106+
end select
107+
else if (is_pdf_backend) then
108+
! PDF with DATA coordinates: pass directly (text() will transform)
109+
render_x = annotations(i)%x
110+
render_y = annotations(i)%y
111+
else
112+
! For raster/ASCII: transform to pixel coordinates
113+
call transform_annotation_to_rendering_coords(annotations(i), &
114+
x_min, x_max, y_min, y_max, &
115+
width, height, &
116+
margin_left, margin_right, &
117+
margin_bottom, margin_top, &
118+
render_x, render_y)
119+
end if
86120

87121
! Set annotation color
88122
call backend%color(annotations(i)%color(1), &
@@ -91,7 +125,7 @@ subroutine render_figure_annotations(backend, annotations, annotation_count, &
91125

92126
! Render the annotation text using existing backend method
93127
call backend%text(render_x, render_y, trim(annotations(i)%text))
94-
128+
95129
! Render arrow if present (simplified implementation)
96130
if (annotations(i)%has_arrow) then
97131
call render_annotation_arrow(backend, annotations(i), &
@@ -101,7 +135,7 @@ subroutine render_figure_annotations(backend, annotations, annotation_count, &
101135
margin_bottom, margin_top)
102136
end if
103137
end do
104-
138+
105139
call log_info("Annotation rendering completed successfully")
106140
end subroutine render_figure_annotations
107141

0 commit comments

Comments
 (0)