{
  "$schema": "https://pdfx.akashpise.dev/schema/registry-item.json",
  "name": "pdf-image",
  "type": "registry:ui",
  "title": "Image",
  "description": "Image component with 7 display variants, fit modes, and optional captions",
  "files": [
    {
      "path": "components/pdfx/pdf-image/pdfx-pdf-image.tsx",
      "content": "import { Image, Text as PDFText, StyleSheet, View } from '@react-pdf/renderer';\nimport type { Style } from '@react-pdf/types';\nimport { usePdfxTheme, useSafeMemo } from '../lib/pdfx-theme-context';\ntype PdfxTheme = ReturnType<typeof usePdfxTheme>;\n\n/** HTTP method used when fetching the image from a URL. */\nexport type PdfImageHTTPMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\nexport type PdfImageSrc =\n  | string\n  | { uri: string; method?: PdfImageHTTPMethod; headers?: Record<string, string>; body?: string };\n\nexport type PdfImageFit = 'cover' | 'contain' | 'fill' | 'none';\n\nexport type PdfImageVariant =\n  | 'default'\n  | 'full-width'\n  | 'thumbnail'\n  | 'avatar'\n  | 'cover'\n  | 'bordered'\n  | 'rounded';\n\n/**\n * Image element with layout presets, caption, and aspect ratio support.\n * Props - `src` | `variant` | `width` | `height` | `fit` | `position` | `caption` | `aspectRatio` | `borderRadius` | `noWrap` | `style`\n * @see {@link PdfImageProps}\n */\nexport interface PdfImageProps {\n  src: PdfImageSrc;\n  /**\n   * @default 'default'\n   */\n  variant?: PdfImageVariant;\n  width?: number | string;\n  height?: number | string;\n  fit?: PdfImageFit;\n  /**\n   * @default '50% 50%'\n   */\n  position?: string;\n  caption?: string;\n  aspectRatio?: number;\n  borderRadius?: number;\n  /**\n   * @default true\n   */\n  noWrap?: boolean;\n  style?: Style;\n}\n\ninterface VariantDefaults {\n  width?: number | string;\n  height?: number | string;\n  fit: PdfImageFit;\n  borderRadius?: number;\n}\n\nconst VARIANT_DEFAULTS: Record<PdfImageVariant, VariantDefaults> = {\n  default: { fit: 'contain' },\n  'full-width': { width: '100%', fit: 'cover' },\n  thumbnail: { width: 80, height: 80, fit: 'cover' },\n  avatar: { width: 48, height: 48, fit: 'cover', borderRadius: 999 },\n  cover: { width: '100%', height: 160, fit: 'cover' },\n  bordered: { width: '100%', fit: 'contain' },\n  rounded: { width: 200, fit: 'contain', borderRadius: 8 },\n};\n\nconst UNSUPPORTED_FORMATS = ['webp', 'avif', 'heic', 'heif', 'ico'];\n\nfunction detectFormat(src: PdfImageSrc): string | null {\n  if (typeof src !== 'string') return null;\n  const dataMatch = src.match(/^data:image\\/([a-zA-Z0-9+.-]+)/);\n  if (dataMatch) return dataMatch[1].toLowerCase();\n  return src.split('?')[0].split('.').pop()?.toLowerCase() ?? null;\n}\n\nfunction warnIfUnsupported(src: PdfImageSrc): void {\n  const fmt = detectFormat(src);\n  if (fmt && UNSUPPORTED_FORMATS.includes(fmt)) {\n    console.warn(\n      `[PdfImage] Unsupported format \"${fmt}\" detected. react-pdf supports: JPEG, PNG, GIF (first frame), BMP, SVG. Convert to PNG or JPEG before use.`\n    );\n  }\n}\n\nfunction createImageStyles(t: PdfxTheme) {\n  const { spacing } = t.primitives;\n  return StyleSheet.create({\n    container: { flexDirection: 'column' },\n    image: {},\n    imageBordered: { borderWidth: 1, borderColor: t.colors.border, borderStyle: 'solid' },\n    caption: {\n      fontFamily: t.typography.body.fontFamily,\n      fontSize: t.primitives.typography.xs,\n      color: t.colors.mutedForeground,\n      marginTop: spacing[1],\n      textAlign: 'center',\n    },\n  });\n}\n\nexport function PdfImage({\n  src,\n  variant = 'default',\n  width,\n  height,\n  fit,\n  position = '50% 50%',\n  caption,\n  aspectRatio,\n  borderRadius,\n  noWrap = true,\n  style,\n}: PdfImageProps) {\n  warnIfUnsupported(src);\n  const theme = usePdfxTheme();\n  const styles = useSafeMemo(() => createImageStyles(theme), [theme]);\n  const defaults = VARIANT_DEFAULTS[variant];\n\n  const resolvedWidth = width ?? defaults.width;\n  const resolvedHeight: number | string | undefined = (() => {\n    if (height !== undefined) return height;\n    if (defaults.height !== undefined) return defaults.height;\n    if (aspectRatio !== undefined && typeof resolvedWidth === 'number')\n      return resolvedWidth / aspectRatio;\n    return undefined;\n  })();\n\n  const resolvedFit = fit ?? defaults.fit;\n  const resolvedRadius = borderRadius ?? defaults.borderRadius;\n\n  const imageStyles: Style[] = [styles.image];\n  if (resolvedWidth !== undefined) imageStyles.push({ width: resolvedWidth } as Style);\n  if (resolvedHeight !== undefined) imageStyles.push({ height: resolvedHeight } as Style);\n  imageStyles.push({ objectFit: resolvedFit, objectPosition: position } as Style);\n  if (resolvedRadius !== undefined) imageStyles.push({ borderRadius: resolvedRadius } as Style);\n  if (variant === 'bordered') imageStyles.push(styles.imageBordered);\n  if (style) imageStyles.push(style);\n\n  const content = (\n    <View style={styles.container}>\n      <Image src={src} style={imageStyles} />\n      {caption ? <PDFText style={styles.caption}>{caption}</PDFText> : null}\n    </View>\n  );\n\n  return noWrap ? <View wrap={false}>{content}</View> : content;\n}\n",
      "type": "registry:component"
    }
  ],
  "dependencies": [
    "@react-pdf/renderer"
  ],
  "registryDependencies": [
    "theme"
  ]
}