Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
