layout: fix dimension of inline images, closes #1555 (#1560)

This commit is contained in:
Huang Xin
2025-07-09 15:52:51 +08:00
committed by GitHub
parent bcfe1b8830
commit aafcab8232
3 changed files with 53 additions and 10 deletions
@@ -13,7 +13,12 @@ import { usePagination } from '../hooks/usePagination';
import { useFoliateEvents } from '../hooks/useFoliateEvents';
import { useProgressSync } from '../hooks/useProgressSync';
import { useProgressAutoSave } from '../hooks/useProgressAutoSave';
import { applyTranslationStyles, getStyles, transformStylesheet } from '@/utils/style';
import {
applyImageStyle,
applyTranslationStyle,
getStyles,
transformStylesheet,
} from '@/utils/style';
import { mountAdditionalFonts } from '@/utils/font';
import { getBookDirFromLanguage, getBookDirFromWritingMode } from '@/utils/book';
import { useUICSS } from '@/hooks/useUICSS';
@@ -129,6 +134,8 @@ const FoliateViewer: React.FC<{
mountAdditionalFonts(detail.doc, isCJKLang(bookData.book?.primaryLanguage));
applyImageStyle(detail.doc);
// Inline scripts in tauri platforms are not executed by default
if (viewSettings.allowScript && isTauriAppPlatform()) {
evalInlineScripts(detail.doc);
@@ -242,7 +249,7 @@ const FoliateViewer: React.FC<{
const height = viewHeight - insets.top - insets.bottom;
book.transformTarget?.addEventListener('data', getDocTransformHandler({ width, height }));
view.renderer.setStyles?.(getStyles(viewSettings));
applyTranslationStyles(viewSettings);
applyTranslationStyle(viewSettings);
const animated = viewSettings.animated!;
const maxColumnCount = viewSettings.maxColumnCount!;
+18 -1
View File
@@ -84,7 +84,24 @@ export const getPosition = (
const [sx, , , sy] = match?.[1]?.split(/\s*,\s*/)?.map((x) => parseFloat(x)) ?? [];
const frame = frameElement?.getBoundingClientRect() ?? { top: 0, left: 0 };
const rects = Array.from(target.getClientRects());
let padding = { top: 0, right: 0, bottom: 0, left: 0 };
if ('nodeType' in target && target.nodeType === 1) {
const computedStyle = window.getComputedStyle(target);
padding = {
top: parseInt(computedStyle.paddingTop, 10) || 0,
right: parseInt(computedStyle.paddingRight, 10) || 0,
bottom: parseInt(computedStyle.paddingBottom, 10) || 0,
left: parseInt(computedStyle.paddingLeft, 10) || 0,
};
}
const rects = Array.from(target.getClientRects()).map((rect) => {
return {
top: rect.top + padding.top,
right: rect.right - padding.right,
bottom: rect.bottom - padding.bottom,
left: rect.left + padding.left,
};
});
const first = frameRect(frame, rects[0], sx, sy);
const last = frameRect(frame, rects.at(-1), sx, sy);
+26 -7
View File
@@ -211,6 +211,11 @@ const getLayoutStyles = (
width: auto;
background-color: transparent !important;
}
/* enlarge the clickable area of links */
a {
padding: 10px;
margin: -10px;
}
p, blockquote, dd, div:not(:has(*:not(b, a, em, i, strong, u, span))) {
line-height: ${lineSpacing} ${overrideLayout ? '!important' : ''};
word-spacing: ${wordSpacing}px ${overrideLayout ? '!important' : ''};
@@ -286,13 +291,12 @@ const getLayoutStyles = (
}
/* inline images without dimension */
p > img, span > img, sup img {
sup img {
height: 1em;
}
p:has(> img:only-child) img, span:has(> img:only-child) img,
p:has(> a:first-child + img:last-child) img,
span:has(> a:first-child + img:last-child) img {
height: auto;
img.has-text-siblings {
height: 1em;
vertical-align: text-bottom;
}
.ie6 img {
width: auto;
@@ -328,6 +332,8 @@ export const getFootnoteStyles = () => `
a:any-link {
text-decoration: none;
padding: unset;
margin: unset;
}
ol {
@@ -446,8 +452,8 @@ export const getStyles = (viewSettings: ViewSettings, themeCode?: ThemeCode) =>
return `${layoutStyles}\n${fontStyles}\n${colorStyles}\n${translationStyles}\n${userStylesheet}`;
};
export const applyTranslationStyles = (viewSettings: ViewSettings) => {
const styleId = 'translation-styles';
export const applyTranslationStyle = (viewSettings: ViewSettings) => {
const styleId = 'translation-style';
const existingStyle = document.getElementById(styleId);
if (existingStyle) {
@@ -509,3 +515,16 @@ export const transformStylesheet = (vw: number, vh: number, css: string) => {
.replace(/[\s;]color\s*:\s*rgb\(0,\s*0,\s*0\)/gi, 'color: var(--theme-fg-color)');
return css;
};
export const applyImageStyle = (document: Document) => {
document.querySelectorAll('img').forEach((img) => {
const parent = img.parentNode;
if (!parent || parent.nodeType !== Node.ELEMENT_NODE) return;
const hasTextSiblings = Array.from(parent.childNodes).some(
(node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim(),
);
if (hasTextSiblings) {
img.classList.add('has-text-siblings');
}
});
};