宽度为硬约束:输出图像宽度必为 box_w(默认 2000px)。 多 logo 按比例统一缩放,拼接后刚好占满 box_w(包含间距)。 高度由比例自然决定,可能 < box_h,也可能 > box_h(甚至 > 2*box_h),不会再二次压缩。 透明背景,输出 PNG。
(logo_paths, out_path: Path, box_w=2000, box_h=476, gap=16)
| 96 | return files |
| 97 | |
| 98 | def _compose_logos_horizontally(logo_paths, out_path: Path, box_w=2000, box_h=476, gap=16): |
| 99 | """ |
| 100 | 宽度为硬约束:输出图像宽度必为 box_w(默认 2000px)。 |
| 101 | 多 logo 按比例统一缩放,拼接后刚好占满 box_w(包含间距)。 |
| 102 | 高度由比例自然决定,可能 < box_h,也可能 > box_h(甚至 > 2*box_h),不会再二次压缩。 |
| 103 | 透明背景,输出 PNG。 |
| 104 | """ |
| 105 | # 读取图片 |
| 106 | imgs = [] |
| 107 | for p in logo_paths: |
| 108 | p = Path(p) |
| 109 | if p.exists() and p.is_file(): |
| 110 | imgs.append(Image.open(p).convert("RGBA")) |
| 111 | n = len(imgs) |
| 112 | if n == 0: |
| 113 | raise RuntimeError("No logo images found.") |
| 114 | |
| 115 | # 原始总宽度(不含 gap);拼接总宽 = sum(w_i) + gap*(n-1) |
| 116 | widths = [im.width for im in imgs] |
| 117 | heights = [im.height for im in imgs] |
| 118 | sum_w = sum(widths) |
| 119 | if sum_w <= 0: |
| 120 | raise RuntimeError("All logo images have zero width.") |
| 121 | |
| 122 | # 计算统一缩放比例,使:sum(w_i * s) + gap*(n-1) == box_w |
| 123 | # => s = (box_w - gap*(n-1)) / sum_w |
| 124 | total_gap = max(0, gap * (n - 1)) |
| 125 | if box_w <= total_gap: |
| 126 | raise ValueError(f"box_w({box_w}) too small vs total gaps({total_gap}). Increase box_w or reduce gap.") |
| 127 | s = (box_w - total_gap) / float(sum_w) |
| 128 | |
| 129 | # 按统一比例缩放(四舍五入到整数像素,避免累计误差) |
| 130 | resized = [] |
| 131 | scaled_widths = [] |
| 132 | scaled_heights = [] |
| 133 | for im, w, h in zip(imgs, widths, heights): |
| 134 | nw = max(1, int(round(w * s))) |
| 135 | nh = max(1, int(round(h * s))) |
| 136 | resized.append(im.resize((nw, nh), Image.LANCZOS)) |
| 137 | scaled_widths.append(nw) |
| 138 | scaled_heights.append(nh) |
| 139 | |
| 140 | # 由于整数取整,可能出现总宽 != box_w - total_gap;对若干图微调 1px 以精确对齐 |
| 141 | current_sum_w = sum(scaled_widths) |
| 142 | diff = (box_w - total_gap) - current_sum_w |
| 143 | # 按从宽到窄/从大到小顺序均匀分配像素误差 |
| 144 | if diff != 0: |
| 145 | order = sorted(range(n), key=lambda i: scaled_widths[i], reverse=(diff > 0)) |
| 146 | idx = 0 |
| 147 | step = 1 if diff > 0 else -1 |
| 148 | remaining = abs(diff) |
| 149 | while remaining > 0 and n > 0: |
| 150 | i = order[idx % n] |
| 151 | new_w = scaled_widths[i] + step |
| 152 | if new_w >= 1: |
| 153 | scaled_widths[i] = new_w |
| 154 | resized[i] = resized[i].resize((new_w, resized[i].height), Image.LANCZOS) |
| 155 | remaining -= 1 |
no test coverage detected