
    (<i>                    V   d Z ddlmZ ddlZddlZddlZddlmZ ddlZddl	m
Z
 ddlmZmZmZ ej                  j!                  d e ee      j&                               ddlZdZeZdZd	Zd
Zedz  Zedz  Zedz  Zedz  ZdZdZdZ dZ!dZ"ddZ#ddZ$ddZ%ddZ&e'dk(  r e&        yy)u   Meta 캐러셀 광고 A-2 (Problem 슬라이드) 하이브리드 이미지 생성.

Gemini API로 배경(어두운 추상 배경 + 미니멀 아이콘 실루엣) 생성 →
Playwright HTML 오버레이로 한글 카피 합성.
출력: 1080x1080 PNG
    )annotationsN)Path)sync_playwright)WORKSPACE_ROOTFONT_DIR
CTA_MIN_PX   X   gzG?gffffff?z%output/meta-ads/a-group-v6/productionz_bg_a2_problem.jpgzmeta-A2-problem.pngz_a2_problem_template.htmlu#  Dark moody abstract background for a Meta carousel advertisement. Color palette: deep charcoal (#2D3748) transitioning to near-black — slightly lighter than pure black but very dark and oppressive. Subtle faint line-art icons barely visible in the darkness: (1) an empty contacts / address-book icon at the top — hollow, no entries inside; (2) a speech bubble filled with question marks in the middle; (3) a declining line graph / downward trend arrow at the bottom. All three icons are very faint, ghost-like, almost invisible — just visible enough to feel their presence without distracting from overlaid text. Film grain texture across the entire surface for depth and tactile feel. No people, no hands, no faces, no bright elements, no warm colors. No text, no watermarks, no numbers. Slightly lighter than pure black — a heavy, claustrophobic charcoal. Mood: isolation, dead ends, oppression, silence. Minimal contrast — nearly monochrome dark charcoal throughout. Square 1:1 composition 1080x1080. Cinematic, high quality, minimal digital art.zgemini-3.1-flash-image-previewzgemini-3-pro-image-previewz0https://generativelanguage.googleapis.com/v1betaz3https://www.googleapis.com/auth/generative-languagec            	        t         j                         rt        dt                 t         S t        d       t        j                         } t        dt        |        d       t         dt         d}d|  dd	}d
dt        igigdddgid}t        dt         d       t        j                         }t        j                  |||d      }|j                  dv rHt        d|j                   dt                t         dt         d}t        j                  |||d      }|j                          t        j                         |z
  }|j                         }|j!                  dg       }|st#        dt%        |      dd        |d   j!                  di       j!                  d
g       }	t'        d |	D        d      }
|
5|	D cg c]  }d|v s|j!                  dd       }}t#        d|dd        |
d    j!                  d!d"      }t)        j*                  |
d    d#         }d$|v rd%nd&}t         j-                  |      }|j/                  |       t        d'|j0                   d(t        |      d)d*|d+d,       |S c c}w )-uG   Gemini API로 배경 이미지를 생성하고 JPEG로 저장합니다.u,   [배경] 캐시된 배경 이미지 사용: u(   [배경] Gemini API 토큰 획득 중...u   [배경] 토큰 획득 완료 (z chars)z/models/z:generateContentzBearer zapplication/json)AuthorizationzContent-TypepartstextresponseModalitiesIMAGETEXT)contentsgenerationConfigu1   [배경] 이미지 생성 요청 중... (모델: )i,  )headersjsontimeout)i  i  u$   [배경] 모델 접근 불가 (HTTP z). Fallback: 
candidatesu   candidates 없음: Nr   contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     ]/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/gen_production_a2_problem.py	<genexpr>z&generate_background.<locals>.<genexpr>n   s     =Q<1+<q=s   	 u'   이미지 데이터 없음. 텍스트:    r   mimeTypez
image/jpegdatajpegz.jpgz.pngu   [배경] 완료:  (,z bytes, z.1fu   초))BG_JPEGexistsprintgcloud_authget_access_tokenlenGEMINI_API_BASEMODEL_ID	BG_PROMPTtimerequestspoststatus_codeFALLBACK_MODEL_IDraise_for_statusr   getRuntimeErrorstrnextbase64	b64decodewith_suffixwrite_bytesname)tokenurlr   payloadstartrespelapsedr$   r   r   
image_partr   textsmimeimage_bytesext	save_paths                    r   generate_backgroundrL   G   s   ~~<WIFG	
45((*E	+CJ<w
?@XhZ/?
@C"5'**G
  34561GV3DEG
 
=hZq
IJIIKE==gGSID :%4T5E5E4FmTeSfgh !*;)<<LM}}S'MiikE!G99;D,+J0T4C0ABCCqMi,00"=E=%=tDJ,1AqVq[vr"AADU2AYKPQQl#''
LAD"":l#;F#CDKdN&C##C(I+&	inn-RK0@/C8GTW=X\
]^ Bs   	JJc                    dt          dt          dt          dt          dt          d|  dt         dt         d	t         d
t         dt         dt         dt
         dS )u>   A-2 Problem 슬라이드 HTML 오버레이를 빌드합니다.z<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
  @font-face {
    font-family: 'Pretendard';
    src: url('file://z/Pretendard-Black.otf') format('opentype');
    font-weight: 900;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://z/Pretendard-ExtraBold.otf') format('opentype');
    font-weight: 800;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://z/Pretendard-Bold.otf') format('opentype');
    font-weight: 700;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://z/Pretendard-Medium.otf') format('opentype');
    font-weight: 500;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://u  /Pretendard-Regular.otf') format('opentype');
    font-weight: 400;
  }

  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

  body {
    width: 1080px;
    height: 1080px;
    overflow: hidden;
    background: #1A202C;
  }

  .canvas {
    width: 1080px;
    height: 1080px;
    position: relative;
    font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
    overflow: hidden;
  }

  /* ─── 배경 이미지 ─── */
  .bg {
    position: absolute;
    inset: 0;
    background: url('file://u  ') center center / cover no-repeat;
  }

  /* ─── 전체 어둠 오버레이 — A-1보다 더 무겁게 ─── */
  .overlay-dark {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to bottom,
      rgba(15, 18, 28, 0.88) 0%,
      rgba(15, 18, 28, 0.60) 20%,
      rgba(15, 18, 28, 0.50) 45%,
      rgba(10, 12, 20, 0.72) 72%,
      rgba(6, 8, 14, 0.97) 100%
    );
  }

  /* ─── 상단 미니멀 공간 (Top 15%) — 전환 강조 ─── */
  /* 아이콘 영역: 1080 * 0.15 = 162px */
  .top-icons {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 162px;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding-left: 64px;
    gap: 48px;
  }

  /* SVG 아이콘 — line-art 스타일 */
  .icon-svg {
    opacity: 0.28;
    flex-shrink: 0;
  }

  /* ─── 헤드라인 영역 (Center 50%) — LEFT 정렬 ─── */
  /* Top 15% ~ 65%: y=162 ~ y=702 */
  .headline-wrap {
    position: absolute;
    top: 162px;
    left: 64px;
    right: 64px;
    height: 540px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
  }

  .headline {
    font-size: z?px;
    font-weight: 900;
    color: #F7F8FA;
    line-height: u  ;
    letter-spacing: -3.5px;
    word-break: keep-all;
    text-align: left;
    text-shadow: 0 2px 28px rgba(0,0,0,0.7);
  }

  /* "방법이 없었던 거다." — 포인트 컬러 강조 */
  .headline .accent {
    color: #D97706;
    text-shadow:
      0 0 40px rgba(217, 119, 6, 0.40),
      0 2px 24px rgba(0,0,0,0.6);
    font-weight: 900;
  }

  /* ─── 하단 서브카피 영역 (Bottom 35%) ─── */
  /* y=702 ~ y=1080 */
  .bottom-zone {
    position: absolute;
    top: 702px;
    left: 0;
    right: 0;
    bottom: 0;
  }

  /* 하단 어둠 강화 — 텍스트 가독성 보강 */
  .bottom-gradient {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to bottom,
      rgba(6, 8, 14, 0.0) 0%,
      rgba(6, 8, 14, 0.75) 35%,
      rgba(6, 8, 14, 0.95) 100%
    );
  }

  .subcopy-wrap {
    position: absolute;
    bottom: 80px;
    left: 64px;
    right: 72px;
  }

  /* 1번 서브카피 문장 */
  .subcopy-line1 {
    font-size: zQpx;
    font-weight: 400;
    color: rgba(200, 205, 216, 0.72);
    line-height: u   ;
    letter-spacing: -0.8px;
    word-break: keep-all;
    margin-bottom: 20px;
  }

  /* 2번 서브카피 문장 — 여운을 위한 분리 */
  .subcopy-line2 {
    font-size: zQpx;
    font-weight: 500;
    color: rgba(218, 220, 230, 0.82);
    line-height: u   ;
    letter-spacing: -0.8px;
    word-break: keep-all;
  }

  /* 페이지 인디케이터 2/5 */
  .page-indicator {
    position: absolute;
    bottom: 32px;
    right: 52px;
    font-size: u  px;
    font-weight: 500;
    color: rgba(180, 185, 200, 0.45);
    letter-spacing: 2px;
  }

</style>
</head>
<body>
<div class="canvas">

  <!-- 배경 이미지 -->
  <div class="bg"></div>

  <!-- 전체 어둠 오버레이 -->
  <div class="overlay-dark"></div>

  <!-- 상단 미니멀 아이콘 영역 (Top 15%) -->
  <div class="top-icons">
    <!-- 빈 연락처 아이콘 -->
    <svg class="icon-svg" width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
      <rect x="4" y="8" width="40" height="40" rx="4" stroke="#C0C8D8" stroke-width="2.5"/>
      <circle cx="24" cy="22" r="6" stroke="#C0C8D8" stroke-width="2.5"/>
      <path d="M12 40c0-6.627 5.373-12 12-12h0c6.627 0 12 5.373 12 12" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round"/>
      <line x1="46" y1="14" x2="52" y2="14" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round"/>
      <line x1="46" y1="22" x2="52" y2="22" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round"/>
      <line x1="46" y1="30" x2="52" y2="30" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round"/>
    </svg>

    <!-- 물음표 가득한 말풍선 아이콘 -->
    <svg class="icon-svg" width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M6 8h44a2 2 0 0 1 2 2v26a2 2 0 0 1-2 2H20L8 50V38H6a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2z" stroke="#C0C8D8" stroke-width="2.5" stroke-linejoin="round"/>
      <text x="15" y="31" font-family="sans-serif" font-size="18" fill="#C0C8D8" opacity="0.9">???</text>
    </svg>

    <!-- 하락 그래프 아이콘 -->
    <svg class="icon-svg" width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
      <polyline points="6,12 6,50 50,50" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
      <polyline points="10,18 22,28 32,24 46,44" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
      <polyline points="40,44 46,44 46,38" stroke="#C0C8D8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
  </div>

  <!-- 헤드라인 (Center 50%) — LEFT 정렬, 16px 마진 느낌 -->
  <div class="headline-wrap">
    <div class="headline">
      문제는 노력이 아니다.<br>
      <span class="accent">방법이 없었던 거다.</span>
    </div>
  </div>

  <!-- 하단 서브카피 영역 -->
  <div class="bottom-zone">
    <div class="bottom-gradient"></div>
    <div class="subcopy-wrap">
      <p class="subcopy-line1">지인 명단은 바닥났고, 알려줄 멘토도 없었다.</p>
      <p class="subcopy-line2">혼자 버티는 건 미덕이 아니라 손실이다.</p>
    </div>
  </div>

  <!-- 페이지 인디케이터 -->
  <div class="page-indicator">2 / 5</div>

</div>
</body>
</html>)r   
_SIZE_88PX_LH_1_18_CTA_PX_LH_1_65
_SIZE_26PXbg_paths    r   
build_htmlrU      s     Z  
 Z  
 Z  
 Z  
 Z  : %I 5&j |   /^ y    y   
 | Aox x    c                   t        t        | j                                     }t        j	                  |d       t        dt                t        d       t               5 }|j                  j                         }	 |j                  ddd      }|j                  dt        j                          d	
       |j                  d       t        j                  j                  dd       |j                  t        t              d       |j!                          	 ddd       t        j#                         j$                  }t        dt         d|dd       y# |j!                          w xY w# 1 sw Y   TxY w)uN   HTML 오버레이를 Playwright로 캡처하여 최종 PNG를 저장합니다.zutf-8)encodingu&   [오버레이] HTML 템플릿 저장: u*   [오버레이] Playwright 캡처 시작...i8  )widthheight)viewportzfile://networkidle)
wait_untili	  Tparentsexist_okpng)pathtypeNu   [오버레이] 완료: r&   r'   z bytes))rU   r9   resolve	HTML_TEMP
write_textr*   r   chromiumlaunchnew_pagegotowait_for_timeout
OUTPUT_PNGparentmkdir
screenshotclosestatst_size)rT   html_contentr   browserpagesizes         r   capture_overlayrw     s0   c'//"345L8	29+
>?	
67		 
a**##%	##tt-L#MDII	 1 1 345-IP!!$'##D4#@OOZuO=MMO
 ??$$D	#J<r$q
AB MMO
 
s%   !E0=BEE0E--E00E9c                    t        d       t        d       t        dt                t        d       t        j                  dd       t	               } t        |        t        d       t        dt                t        d       y )Nz<============================================================u2   Meta 캐러셀 A-2 Problem 이미지 생성 시작u   출력: Tr^   z=
============================================================u   완료: )r*   rl   
OUTPUT_DIRrn   rL   rw   rS   s    r   mainrz     sm    	(O	
>?	HZL
!"	(OTD1 "#G G	/	HZL
!"	(OrV   __main__)returnr   )rT   r9   r|   r9   )rT   r   r|   None)r|   r}   )(__doc__
__future__r   r;   sysr1   pathlibr   r2   playwright.sync_apir   
gen_configr   r   r   rb   insertr9   __file__rm   r+   rR   rP   rN   rO   rQ   ry   r(   rl   re   r0   r/   r5   r.   GEMINI_SCOPErL   rU   rw   rz   __name__r   rV   r   <module>r      s    #  
    / ; ; 3tH~,,- . 


 EE

+
+//
44	4 
0 ,0 DD3vzDC8& zF rV   