comparison mupdf-source/source/pdf/pdf-layout.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "mupdf/pdf.h"
25 #include <float.h>
26 #include <math.h>
27
28 #define LINE_LIMIT (100)
29 #define LINE_HEIGHT (1.2f)
30 struct line { const char *a, *b; };
31
32 struct font_info
33 {
34 fz_context *ctx;
35 fz_font *font;
36 float fontsize;
37 };
38
39 static float measure_character(struct font_info *info, int c)
40 {
41 fz_font *font;
42 int gid = fz_encode_character_with_fallback(info->ctx, info->font, c, 0, 0, &font);
43 return fz_advance_glyph(info->ctx, font, gid, 0) * info->fontsize;
44 }
45
46
47 static int break_lines(struct font_info *info, const char *a, struct line *lines, int maxlines, float width, float *maxwidth)
48 {
49 const char *next, *space = NULL, *b = a;
50 int c, n = 0;
51 float space_x, x = 0, w = 0;
52
53 if (maxwidth)
54 *maxwidth = 0;
55
56 while (*b)
57 {
58 next = b + fz_chartorune(&c, b);
59 if (c == '\r' || c == '\n')
60 {
61 if (lines && n < maxlines)
62 {
63 lines[n].a = a;
64 lines[n].b = b;
65 }
66 ++n;
67 if (maxwidth && *maxwidth < x)
68 *maxwidth = x;
69 a = next;
70 x = 0;
71 space = NULL;
72 }
73 else
74 {
75 if (c == ' ')
76 {
77 space = b;
78 space_x = x;
79 }
80
81 w = measure_character(info, c);
82 if (x + w > width)
83 {
84 if (space)
85 {
86 if (lines && n < maxlines)
87 {
88 lines[n].a = a;
89 lines[n].b = space;
90 }
91 ++n;
92 if (maxwidth && *maxwidth < space_x)
93 *maxwidth = space_x;
94 a = next = space + 1;
95 x = 0;
96 space = NULL;
97 }
98 else
99 {
100 if (lines && n < maxlines)
101 {
102 lines[n].a = a;
103 lines[n].b = b;
104 }
105 ++n;
106 if (maxwidth && *maxwidth < x)
107 *maxwidth = x;
108 a = b;
109 x = w;
110 space = NULL;
111 }
112 }
113 else
114 {
115 x += w;
116 }
117 }
118 b = next;
119 }
120
121 if (lines && n < maxlines)
122 {
123 lines[n].a = a;
124 lines[n].b = b;
125 }
126 ++n;
127 if (maxwidth && *maxwidth < x)
128 *maxwidth = x;
129 return n < maxlines ? n : maxlines;
130 }
131
132 static fz_matrix show_string(fz_context *ctx, fz_text *text, fz_font *user_font, fz_matrix trm, const char *s, int len,
133 int wmode, int bidi_level, fz_bidi_direction markup_dir, fz_text_language language)
134 {
135 fz_font *font;
136 int gid, ucs;
137 float adv;
138 int i = 0;
139
140 while (i < len)
141 {
142 i += fz_chartorune(&ucs, s + i);
143 gid = fz_encode_character_with_fallback(ctx, user_font, ucs, 0, language, &font);
144 fz_show_glyph(ctx, text, font, trm, gid, ucs, wmode, bidi_level, markup_dir, language);
145 adv = fz_advance_glyph(ctx, font, gid, wmode);
146 if (wmode == 0)
147 trm = fz_pre_translate(trm, adv, 0);
148 else
149 trm = fz_pre_translate(trm, 0, adv);
150 }
151
152 return trm;
153 }
154
155 fz_text *pdf_layout_fit_text(fz_context *ctx, fz_font *font, fz_text_language lang, const char *str, fz_rect bounds)
156 {
157 fz_text *text = NULL;
158 struct font_info info;
159 struct line *lines;
160 float width = bounds.x1 - bounds.x0;
161 float height = bounds.y1 - bounds.y0;
162
163 lines = fz_malloc_array(ctx, LINE_LIMIT, struct line);
164
165 fz_var(info);
166 fz_try(ctx)
167 {
168 fz_matrix trm;
169 int target_line_count;
170 int line_count, l;
171 float line_len;
172 fz_rect tbounds;
173 float xadj, yadj;
174 fz_text_span *span;
175
176 info.ctx = ctx;
177 info.font = font;
178 info.fontsize = 1;
179
180 /* Find out how many lines the text requires without any wrapping */
181 target_line_count = break_lines(&info, str, lines, LINE_LIMIT, FLT_MAX, &line_len);
182
183 /* Try increasing line counts, which reduces the font size, until the text fits */
184 do
185 {
186 info.fontsize = height / (target_line_count * LINE_HEIGHT);
187 line_count = break_lines(&info, str, lines, LINE_LIMIT, width, &line_len);
188 } while (line_count > target_line_count++);
189
190 trm = fz_scale(info.fontsize, -info.fontsize);
191 trm.e += bounds.x0;
192 trm.f += bounds.y1;
193 text = fz_new_text(ctx);
194 for (l = 0; l < line_count; l++)
195 {
196 show_string(ctx, text, font, trm, lines[l].a, lines[l].b - lines[l].a, 0, 0, FZ_BIDI_LTR, lang);
197 trm = fz_pre_translate(trm, 0.0f, -LINE_HEIGHT);
198 }
199 tbounds = fz_bound_text(ctx, text, NULL, fz_identity);
200 xadj = (bounds.x0 + bounds.x1 - tbounds.x0 - tbounds.x1) / 2.0f;
201 yadj = (bounds.y0 + bounds.y1 - tbounds.y0 - tbounds.y1) / 2.0f;
202 for (span = text->head; span; span = span->next)
203 {
204 int i;
205 for (i = 0; i < span->len; i++)
206 {
207 span->items[i].x += xadj;
208 span->items[i].y += yadj;
209 }
210 }
211 }
212 fz_always(ctx)
213 {
214 fz_free(ctx, lines);
215 }
216 fz_catch(ctx)
217 {
218 fz_drop_text(ctx, text);
219 fz_rethrow(ctx);
220 }
221
222 return text;
223 }