diff --git a/src/lib/editor/extensions/image.ts b/src/lib/editor/extensions/image.ts index 9cb033d..5003c6b 100644 --- a/src/lib/editor/extensions/image.ts +++ b/src/lib/editor/extensions/image.ts @@ -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, 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 { diff --git a/src/lib/import/docx.ts b/src/lib/import/docx.ts index 90916b1..2f62dc3 100644 --- a/src/lib/import/docx.ts +++ b/src/lib/import/docx.ts @@ -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 }; } diff --git a/src/lib/import/odt.ts b/src/lib/import/odt.ts index db057ed..465add3 100644 --- a/src/lib/import/odt.ts +++ b/src/lib/import/odt.ts @@ -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 }; }