fix: fit an imported as-character image to the text column

An inline atom wider than the column is pushed onto a line of its own and
leaves the empty line above it in the document — 5mm per image. A picture the
file sizes to the column lands there anyway once its cm/EMU width is rounded
to whole px, which is how a full-width Word picture triggered it.
This commit is contained in:
Steffen Becker committed 2026-08-08 08:30:05 +00:00
1 parent c34fb4312a
commit 273654640c
3 files changed
+15 -1

No files matched your search

+10
View File
@@ -44,6 +44,16 @@ export function parsePx(value: string | null): number | null {
export const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));
// An as-character image wider than the text column takes a line of its own and leaves an
// empty one above it. Fitting it to the column is where an image the file sizes to the
// column lands anyway, once its cm/EMU width is rounded to whole px (importers).
export function fitInlineImage(attrs: Record<string, unknown>, maxWidthPx: number): void {
const w = attrs.width;
if (typeof w !== 'number' || w <= maxWidthPx) return;
if (typeof attrs.height === 'number') attrs.height = Math.max(1, Math.round((attrs.height * maxWidthPx) / w));
attrs.width = maxWidthPx;
}
// The page text height in px, capping how tall an image can be stretched. Read live
// from the :root vars the editor maintains (orientation/margins change them).
export function pageContentHeightPx(): number {
+3 -1
View File
@@ -4,13 +4,14 @@ import { lengthToPt } from './styleResolver';
import { HEADING_STYLE_OVERRIDES, MAX_HEADING_LEVEL, normalizeColor } from '../export/odt';
import { builtinStyleSheet, DEFAULT_STYLE, type ParaProps, type Style, type StyleSheet, type TextProps } from '../styles/styleSheet';
import { HEADER_SHADE } from '../editor/extensions/tableHeaderRow';
import { fitInlineImage } from '../editor/extensions/image';
import { formatTabStops } from '../editor/extensions/tabStops';
import { tableLookAttr } from '../styles/tableStyles';
import { orderedTypeFromFormat, orderedTypeAttrAt, childCycle, ROOT_ORDERED_CYCLE, type OrderedCycle } from '../utils/orderedListTypes';
import { bulletCharAttr, bulletCharFromDocx } from '../utils/bulletListTypes';
import { DATE_FORMATS, TIME_FORMATS, docxPicture, toDateValue } from '../utils/dateTime';
import { imageDataUrl, type ConvertedImages } from './imageFormats';
import { PX_PER_CM, type PageMargins } from '../storage/pageMargins';
import { PX_PER_CM, cmToPx, type PageMargins } from '../storage/pageMargins';
import type { Orientation } from '../storage/pageOrientation';
import { formatFromCm, type PageFormat } from '../storage/pageFormat';
import { languageFromOdf, NO_LANGUAGE, type DocumentLanguage } from '../storage/documentLanguage';
@@ -1142,6 +1143,7 @@ function convertDrawing(drawing: Element, ctx: Ctx): Node | null {
if (Number.isFinite(rot) && rot) attrs.rotation = ((Math.round(rot / 60000) % 360) + 360) % 360;
if (anchor) attrs.wrap = anchorWrap(anchor, ctx);
else fitInlineImage(attrs, Math.floor(cmToPx(ctx.contentWidthCm)));
return { type: 'image', attrs };
}
+2
View File
@@ -3,6 +3,7 @@ import { StyleResolver, NS, lengthToPt, lengthToCm, layerTextProps, type PropMap
import { HEADING_STYLE_OVERRIDES, MAX_HEADING_LEVEL, ODF_LOOK_ATTRS, normalizeColor } from '../export/odt';
import { builtinStyleSheet, DEFAULT_STYLE, type ParaProps, type Style, type StyleSheet, type TextProps } from '../styles/styleSheet';
import { HEADER_SHADE } from '../editor/extensions/tableHeaderRow';
import { fitInlineImage } from '../editor/extensions/image';
import { formatTabStops } from '../editor/extensions/tabStops';
import { TABLE_REGIONS, tableLookAttr, type TableLook, type TableRegion } from '../styles/tableStyles';
import { orderedTypeFromFormat, orderedTypeAttrAt, childCycle, ROOT_ORDERED_CYCLE, type OrderedCycle } from '../utils/orderedListTypes';
@@ -158,6 +159,7 @@ function convertFrame(frame: Element, ctx: Ctx): Node | null {
const title = frame.getElementsByTagNameNS(NS.svg, 'title')[0]?.textContent;
if (title) attrs.alt = title;
applyFrameRotationAndWrap(frame, attrs, ctx.resolver.graphicProps(frame.getAttributeNS(NS.draw, 'style-name')));
if (!attrs.wrap || attrs.wrap === 'inline') fitInlineImage(attrs, Math.floor(cmToPx(ctx.contentWidthCm)));
return { type: 'image', attrs };
}