
    (<iE                    \   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mZmZ eZdZdZd	ZeZd
ZdZeZ ee      j4                  Zedz  Zedz  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(dZ)d$dZ*d$dZ+	 	 	 	 	 	 	 	 d%dZ,d&dZ-e.dk(  r e-        yy)'u  컨셉 #35 (Bottom-Anchored Content Stack) 프로덕션 배너 생성기
- c35-1200x628.png  (가로형, Meta/Google Ads)
- c35-1200x1200.png (정사각형, Meta Ads)
- c35-1200x628.html
- c35-1200x1200.html

디자인:
- Gemini 야경 사무실 배경
- 하단 네이비 그라디언트 오버레이
- Pretendard 900 헤드라인 + 오렌지 키워드 하이라이트
- 프로스티드 글라스 서브카피 블록 (blur 20px)
- 오렌지 아웃라인 CTA 필 버튼
    )annotationsN)Path)sync_playwright)CORE_METRIC_MIN_PX
CTA_MIN_PXFONT_DIRHEAD_SUB_RATIOWORKSPACE_ROOT,   6   D   gffffff?g333333?z9output/meta-ads/production-banners/35-hybrid-v4-refined-Aoutputz	v4-hybridzbg_c35_landscape.jpgzbg_c35_square.jpgz0https://generativelanguage.googleapis.com/v1betazgemini-3-pro-image-previewzgemini-3.1-flash-image-previewam  Modern Korean office interior at night, dark and moody atmosphere, a desk with a laptop, large window showing city lights in the background, soft ambient lighting from monitors, cool blue-grey color grading, cinematic depth of field, photorealistic, no people, no text, no logos. Wide landscape composition 16:9 aspect ratio, the lower half darker for text overlay.aa  Modern Korean office interior at night, dark and moody atmosphere, a desk with a laptop, large window showing city lights in the background, soft ambient lighting from monitors, cool blue-grey color grading, cinematic depth of field, photorealistic, no people, no text, no logos. Square composition, the lower half significantly darker for text overlay.c                     t         j                  j                  dt        t                     dd l} | j                         S )Nr   )syspathinsertstrBASE_DIRgcloud_authget_access_token)r   s    ^/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/gen_c35_production_banners.pyget_auth_tokenr   H   s*    HHOOAs8}%''))    c                     t         j                  j                  dt        t                     dd l} | j                  d      S )Nr   GEMINI_API_KEY)r   r   r   r   r   r   get_api_key)gas    r   r   r   N   s+    HHOOAs8}%>>*++r   c                    t          d| d}d|  dd}t        j                  |||d      }|j                  dk(  r|S |r+t          d| d	| }d
di}t        j                  |||d      S |S )Nz/models/z:generateContentzBearer zapplication/json)AuthorizationContent-Typei,  )headersjsontimeout   z:generateContent?key=r    )GEMINI_API_BASErequestspoststatus_code)	tokenapi_keymodelpayload
url_bearerr!   respurl_keyhdrs_keys	            r   _call_modelr1   T   s    #$HUG3CDJ%,UG#4FXYG==W7CPD3%&hug5J7)T"$67}}WhWcRRKr   c           	     f   |j                         r-|j                         j                  dkD  rt        d|        |S t        d|j                          t               }t               }dd| igigdddgid	}t        t        fD ]m  }t        d
|        	 t        ||||      }|j                  dv r)t        d|j                   d|j                  dd         X|j                  dk7  r)t        d|j                   d|j                  dd         |j                         }|j                  dg       }	|	st        d       |	d   j                  di       j                  dg       }
t        d |
D        d      }|t        d       t!        j"                  |d   d         }|j$                  j'                  dd       |j)                  |       t        d| dt+        |      d d!       |c S  t-        d"|j                         # t        $ r}t        d|        Y d}~d}~ww xY w)#uZ   Gemini API로 배경 이미지를 생성합니다. 이미 존재하면 재사용합니다.iP  u     [배경] 기존 재사용: u"     [배경] Gemini API 생성 중: partstextresponseModalitiesIMAGETEXT)contentsgenerationConfigu       모델: u       요청 예외: N)i  i  i  u       접근 불가 (z):    r$   z	    HTTP : 
candidatesu       candidates 없음r   contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     r   	<genexpr>z&generate_background.<locals>.<genexpr>   s     A|q/@1As   	u       이미지 파트 없음r?   dataTparentsexist_oku       저장 완료:  (,z bytes)u   배경 생성 실패: )existsstatst_sizeprintnamer   r   MODEL_IDFALLBACK_MODELr1   	Exceptionr(   r4   r"   getnextbase64	b64decodeparentmkdirwrite_byteslenRuntimeError)promptout_pathr)   r*   r,   r+   r.   erD   r<   r3   img_part	img_bytess                r   generate_backgroundr`   a   s@   X]]_44v=-hZ89	.x}}o
>?EmG  01231GV3DEG
 N+ UG$%	ugug>D
 .'(8(8'9TYYt_<MNOs"Id../r$))DS/1BCDYY[XXlB/
)+]&&y"599'2FAeA4H/1$$Xl%;F%CD	dT:Y'#H:RIq/AIJ?B /?
@@;  	's+,	s   H	H0H++H0a  
  @font-face {
    font-family: 'Pretendard';
    src: url('file://{FONT_DIR}/Pretendard-Black.otf') format('opentype');
    font-weight: 900;
    font-style: normal;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://{FONT_DIR}/Pretendard-ExtraBold.otf') format('opentype');
    font-weight: 800;
    font-style: normal;
  }
  @font-face {
    font-family: 'Pretendard';
    src: url('file://{FONT_DIR}/Pretendard-Bold.otf') format('opentype');
    font-weight: 700;
    font-style: normal;
  }
c                    d| j                          }dt         dt         dt         dt         dt
         dt         d| d	S )
u   1200x628 가로형 배너 HTML.file://G<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<style>
uA  

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

  body {
    width: 1200px;
    height: 628px;
    overflow: hidden;
    font-family: 'Pretendard', sans-serif;
    background: #001E3C;
  }

  .card {
    position: relative;
    width: 1200px;
    height: 628px;
    overflow: hidden;
  }

  /* 배경 포토 */
  .bg-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
  }

  /* 하단 그라디언트 오버레이 (50% 높이 = 314px) */
  .gradient-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 314px;
    background: linear-gradient(
      to bottom,
      transparent 0%,
      rgba(0, 30, 60, 0.55) 40%,
      rgba(0, 30, 60, 0.88) 100%
    );
  }

  /* 콘텐츠 스택 (하단 좌측 앵커) */
  .content-stack {
    position: absolute;
    bottom: 48px;
    left: 56px;
    right: 56px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
  }

  /* 메인 헤드라인 */
  .headline {
    font-size: ?px;
    font-weight: 900;
    color: #FFFFFF;
    line-height: ux  ;
    letter-spacing: -1.2px;
    word-break: keep-all;
    text-shadow:
      0 2px 8px rgba(0, 0, 0, 0.70),
      0 4px 24px rgba(0, 30, 60, 0.60);
  }

  .headline .highlight {
    color: #EA580C;
  }

  /* 프로스티드 글라스 서브카피 블록 */
  .sub-copy-block {
    background: rgba(0, 30, 60, 0.45);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.18);
    box-shadow:
      0 4px 32px rgba(0, 30, 60, 0.35),
      inset 0 1px 0 rgba(255, 255, 255, 0.12);
    border-radius: 16px;
    padding: 18px 28px;
  }

  .sub-copy-text {
    font-size: Qpx;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.95);
    line-height: ub  ;
    letter-spacing: -0.5px;
    word-break: keep-all;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.50);
  }

  /* CTA 필 버튼 */
  .cta-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 14px 40px;
    border: 2.5px solid #EA580C;
    border-radius: 9999px;
    background: transparent;
    font-size:    px;
    font-weight: 800;
    color: #EA580C;
    letter-spacing: -0.3px;
    white-space: nowrap;
  }
</style>
</head>
<body>
<div class="card">

  <!-- 배경 포토 -->
  <img class="bg-photo" src="u  " alt="background">

  <!-- 하단 그라디언트 오버레이 -->
  <div class="gradient-overlay"></div>

  <!-- 콘텐츠 스택 (하단 좌측 앵커) -->
  <div class="content-stack">

    <!-- 메인 헤드라인 -->
    <div class="headline">
      열심히는 하는데,<br>
      소득은 <span class="highlight">제자리걸음?</span>
    </div>

    <!-- 프로스티드 글라스 서브카피 블록 -->
    <div class="sub-copy-block">
      <div class="sub-copy-text">정착지원금이 시작을 바꿉니다</div>
    </div>

    <!-- CTA 필 버튼 -->
    <div class="cta-pill">지금 조건 확인하기 →</div>

  </div>
</div>
</body>
</html>)resolve
FONT_FACES
_SIZE_54PX_LH_1_2
_SIZE_44PX	_LH_RATIO_CTA_PXbg_pathbg_uris     r   make_html_1200x628rq      s}    w()*F
  :t |   4 |    y  %X &uS Sr   c                    d| j                          }dt         dt         dt         dt         dt
         dt         d| d	S )
u#   1200x1200 정사각형 배너 HTML.rb   rc   u<  

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

  body {
    width: 1200px;
    height: 1200px;
    overflow: hidden;
    font-family: 'Pretendard', sans-serif;
    background: #001E3C;
  }

  .card {
    position: relative;
    width: 1200px;
    height: 1200px;
    overflow: hidden;
  }

  /* 배경 포토 */
  .bg-photo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
  }

  /* 하단 그라디언트 오버레이 (50% 높이 = 600px) */
  .gradient-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 600px;
    background: linear-gradient(
      to bottom,
      transparent 0%,
      rgba(0, 30, 60, 0.55) 40%,
      rgba(0, 30, 60, 0.88) 100%
    );
  }

  /* 콘텐츠 스택 (하단 앵커) */
  .content-stack {
    position: absolute;
    bottom: 72px;
    left: 64px;
    right: 64px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 28px;
  }

  /* 메인 헤드라인 */
  .headline {
    font-size: rd   u  ;
    letter-spacing: -1.8px;
    word-break: keep-all;
    text-shadow:
      0 2px 8px rgba(0, 0, 0, 0.70),
      0 4px 24px rgba(0, 30, 60, 0.60);
  }

  .headline .highlight {
    color: #EA580C;
  }

  /* 프로스티드 글라스 서브카피 블록 */
  .sub-copy-block {
    background: rgba(0, 30, 60, 0.45);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.18);
    box-shadow:
      0 4px 32px rgba(0, 30, 60, 0.35),
      inset 0 1px 0 rgba(255, 255, 255, 0.12);
    border-radius: 16px;
    padding: 28px 40px;
    align-self: stretch;
  }

  .sub-copy-text {
    font-size: re   ub  ;
    letter-spacing: -0.8px;
    word-break: keep-all;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.50);
  }

  /* CTA 필 버튼 */
  .cta-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 18px 52px;
    border: 2.5px solid #EA580C;
    border-radius: 9999px;
    background: transparent;
    font-size: rf   u  " alt="background">

  <!-- 하단 그라디언트 오버레이 -->
  <div class="gradient-overlay"></div>

  <!-- 콘텐츠 스택 (하단 앵커) -->
  <div class="content-stack">

    <!-- 메인 헤드라인 -->
    <div class="headline">
      열심히는 하는데,<br>
      소득은 <span class="highlight">제자리걸음?</span>
    </div>

    <!-- 프로스티드 글라스 서브카피 블록 -->
    <div class="sub-copy-block">
      <div class="sub-copy-text">정착지원금이 시작을 바꿉니다</div>
    </div>

    <!-- CTA 필 버튼 -->
    <div class="cta-pill">지금 조건 확인하기 →</div>

  </div>
</div>
</body>
</html>)rg   rh   
_METRIC_PX_LH_1_15
_SIZE_68PXrl   rk   rn   s     r   make_html_1200x1200rv   E  s}    w()*F
  :t |   6 |    |  %X &wT Tr   c           
        |j                   j                  dd       |j                  | d       t        d|        t	               5 }|j
                  j                         }	 |j                  ||d      }|j                  d|j                          d	
       |j                  d       |j                   j                  dd       |j                  t        |      ddd||d       |j                         j                  dz  }t        d| d|dd       |j                          	 ddd       y# |j                          w xY w# 1 sw Y   yxY w)uB   HTML을 파일로 저장한 뒤 Playwright로 PNG 캡처합니다.TrE   zutf-8)encodingu     [HTML] 저장: )widthheight)viewportrb   networkidle)
wait_untili	  pngr   )xyry   rz   )r   typeclipi   u     [PNG] 캡처 완료: rH   z.0fz KB)N)rV   rW   
write_textrM   r   chromiumlaunchnew_pagegotorg   wait_for_timeout
screenshotr   rK   rL   close)	html_contenthtml_save_pathry   rz   png_pathrB   browserpagekbs	            r   capture_html_to_pngr     sE    t<lW=	n-
./		 a**##%	##u-O#PDII 6 6 89:}IU!!$'OO!!$!>OOXUQ&BO  ((4/B+H:R3xtDEMMO  MMO s%   	E%B9D8E8E

EEc            	        t        d       t        d       t        d       t        j                  dd       t        j                  dd       i } t        d       	 t	        t
        t              }t        |      }t        d	z  }t        d
z  }	 t        ||dd|       t        |      | d<   t        |      | d<   t        d       	 t	        t        t              }t        |      }t        dz  }t        dz  }		 t        ||dd|	       t        |	      | d<   t        |      | d<   t        d       t        d       | j                         D ]!  \  }
}d|
vrdnd}t        d| d|
 d|        # t        d       y # t        $ r-}t        d|        t        d       t        dz  }Y d }~9d }~ww xY w# t        $ r'}t        d|        t        |      | d<   Y d }~#d }~ww xY w# t        $ r-}t        d|        t        d       t        dz  }Y d }~<d }~ww xY w# t        $ r'}t        d|        t        |      | d<   Y d }~&d }~ww xY w) Nz@================================================================u,   컨셉 #35 프로덕션 배너 생성 시작TrE   u    
[1/2] 1200x628 가로형 배너u!     [경고] 배경 생성 실패: u,     [폴백] 기존 bg.png (1024×1024) 사용z=output/meta-ads/concept-catalog/35-hybrid-v4-refined-A/bg.pngzc35-1200x628.htmlzc35-1200x628.pngi  it  1200x628_png1200x628_htmlu#     [오류] 1200x628 캡처 실패: 1200x628_erroru$   
[2/2] 1200x1200 정사각형 배너zc35-1200x1200.htmlzc35-1200x1200.png1200x1200_png1200x1200_htmlu$     [오류] 1200x1200 캡처 실패: 1200x1200_errorzA
================================================================u   생성 결과:errorOKFAILz  [z] r;   )rM   TMP_DIRrW   
OUTPUT_DIRr`   BG_PROMPT_LANDSCAPEBG_LANDSCAPE_PATHrQ   r
   rq   r   r   BG_PROMPT_SQUAREBG_SQUARE_PATHrv   items)resultsbg_landr]   html_1200x628html_out_628png_out_628bg_sqhtml_1200x1200html_out_1200png_out_1200keyvalstatuss                r   mainr     sW   	(O	
89	(OMM$M.TD1 G 

-.c%&9;LM 'w/M!44L!33K+M<sKP#&{#3#&|#4  

12a#$4nE )/N"66M"55L,NM4|T$'$5 $'$6 ! 
/	
MMO ,S +F82cU"SE*+, 
(OW  c1!56<= #bbc  +3A378$'F !+  a1!56<=!``a  ,4QC89%(V!",s`   E: +F3 G& 9+H :	F0"F++F03	G#<GG#&	H/"HH	I(I

I__main__)returnr   )r   
str | None)
r)   r   r*   r   r+   r   r,   dictr   zrequests.Response)r[   r   r\   r   r   r   )ro   r   r   r   )r   r   r   r   ry   intrz   r   r   r   r   None)r   r   )/__doc__
__future__r   rT   r"   r   pathlibr   r&   playwright.sync_apir   
gen_configr   r   r   r	   r
   rm   rk   ri   ru   rs   rt   rj   rl   __file__rV   r   r   r   r   r   r%   rO   rP   r   r   r   r   r1   r`   rh   rq   rv   r   r   __name__r@   r   r   <module>r      s$   #   
   / _ _ 




	 >  YY

X

+44 11 F/3 P *,
0Aj
,VrWx #-0<@EI2:z zF r   