
    (<i?                        d Z ddlmZ ddl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  ed      Z e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dZdZddZddZddZedk(  r e        yy)u   
Concept #20 v2 — 좌우 분할 솔루션형 (Split-Layout Solution) 광고 이미지 리메이크
사양서 준수: 핵심 메시지 84px+, 보조 메시지 64px+, 최소 40px
출력: sample-v2.png, 20-split-layout-v2.png
    )annotationsN)Path)sync_playwrightz&/home/jay/workspace/tools/ai-image-genzC/home/jay/workspace/output/meta-ads/concept-catalog/20-split-layoutz	bg_v2.pngztemplate_v2.htmlzsample-v2.pngz20-split-layout-v2.pngz0https://generativelanguage.googleapis.com/v1betaz3https://www.googleapis.com/auth/generative-languagea   Korean businessman in dark navy suit, early 30s, standing by large office window, contemplative thoughtful expression looking to the side, moody dramatic lighting with shadows on face, modern corporate office background, cinematic portrait style, upper body shot. The figure is positioned in the LEFT HALF of a square frame, right half intentionally empty space. Dark tones, cool blue-grey office atmosphere, shallow depth of field bokeh background. Photorealistic, editorial photography quality, no text, no logos, 1:1 square format 1080x1080.c            
     <   t         j                  j                  dt        t                     ddl} t        d       | j                  t              }t        dt        |       d       g d}|D ]o  }t        d|        t         d| d	}d
| dd}ddt        igigdddgid}	 t        j                  |||d      }|j                  dv rt        d|j                   d       {|j                          |j#                         }	|	j%                  dg       }
|
st        d       |
d   j%                  di       j%                  dg       }t'        d |D        d      }|st        d       |d   j%                  dd       }t)        j*                  |d   d!         }t,        j/                  |       t        d"t,         d#t        |      d$d%| d&       t,        c S  t1        d'      # t        j                   $ r}t        d| d       Y d}~d}~ww xY w)(u?   Gemini API로 배경 이미지를 생성하고 저장합니다.r   Nu2   [1/3] Gemini API로 배경 이미지 생성 중...u'         SA 토큰 획득 성공 (길이: z chars))zgemini-3-pro-image-previewzgemini-3.1-flash-image-previewz)gemini-2.0-flash-preview-image-generationu         모델 시도: z/models/z:generateContentzBearer zapplication/json)AuthorizationzContent-TypepartstextresponseModalitiesIMAGETEXT)contentsgenerationConfigx   )headersjsontimeout)i  i  z      HTTP u    — 다음 모델로 시도u         HTTP 오류: 
candidatesu3         candidates 없음 — 다음 모델로 시도contentc              3  *   K   | ]  }d |v s|  yw)
inlineDataN ).0ps     Y/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/generate_concept20_v2.py	<genexpr>z&generate_background.<locals>.<genexpr>[   s     A|q/@1As   	u<         이미지 데이터 없음 — 다음 모델로 시도r   mimeTypez
image/jpegdatau         배경 저장:  (,z bytes, mime=)u>   모든 Gemini 모델 실패 — 배경 이미지 생성 불가)syspathinsertstrBASE_DIRgcloud_authprintget_service_account_tokenGEMINI_SCOPElenGEMINI_API_BASE	BG_PROMPTrequestspoststatus_coderaise_for_status	HTTPErrorr   getnextbase64	b64decode
BG_V2_PATHwrite_bytesRuntimeError)r&   tokenmodelsmodel_idurlr   payloadresper   r   r   
image_part	mime_typeimage_bytess                  r   generate_backgroundrC   /   s.   HHOOAs8}%	
>?11,?E	3CJ<w
GHF  &%hZ01 !(3CD&ug..

 "VY$7#89:!57H I

	==gGSQD:-D$4$4#55QRS!!#
 yy{XXlB/
GH1!!)R044WbAAeA4H
PQ|,00\J	&&z,'?'GH{+%j\C4DQ3G}U^T__`abM&P W
XX- !! 	's*FGH	s   *A G.+G..HHHu  <!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <style>
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-Black.otf') format('opentype');
      font-weight: 900;
    }}
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-ExtraBold.otf') format('opentype');
      font-weight: 800;
    }}
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-Bold.otf') format('opentype');
      font-weight: 700;
    }}
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-SemiBold.otf') format('opentype');
      font-weight: 600;
    }}
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-Medium.otf') format('opentype');
      font-weight: 500;
    }}
    @font-face {{
      font-family: 'Pretendard';
      src: url('file:///home/jay/.local/share/fonts/Pretendard/Pretendard-Regular.otf') format('opentype');
      font-weight: 400;
    }}

    * {{ margin: 0; padding: 0; box-sizing: border-box; }}
    body {{
      width: 1080px;
      height: 1080px;
      overflow: hidden;
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      -webkit-font-smoothing: antialiased;
    }}

    /* ── 컨테이너: 좌우 50% 분할 ── */
    .container {{
      display: flex;
      width: 1080px;
      height: 1080px;
    }}

    /* ── 좌측 패널: 540px, 인물 사진 + 비네트 + 텍스트 ── */
    .left-panel {{
      width: 540px;
      height: 1080px;
      position: relative;
      overflow: hidden;
    }}
    .left-panel img {{
      width: 100%;
      height: 100%;
      object-fit: cover;
      filter: saturate(0.7) brightness(0.88);
    }}
    /* 어두운 비네트 — 하단으로 갈수록 어둡게 */
    .left-vignette {{
      position: absolute;
      inset: 0;
      background:
        linear-gradient(to bottom,
          rgba(10,18,28,0.10) 0%,
          rgba(10,18,28,0.25) 50%,
          rgba(10,18,28,0.82) 100%
        );
    }}
    /* 좌측 하단 텍스트 블록 */
    .left-text {{
      position: absolute;
      bottom: 60px;
      left: 40px;
      right: 24px;
    }}
    .left-line {{
      display: block;
      color: #FFFFFF;
      font-size: 64px;
      font-weight: 700;
      line-height: 1.32;
      letter-spacing: -1.5px;
      text-shadow: 0 2px 12px rgba(0,0,0,0.80), 0 4px 24px rgba(0,0,0,0.60);
      word-break: keep-all;
    }}

    /* ── 우측 패널: 540px, 따뜻한 크림 배경 + 해결책 ── */
    .right-panel {{
      width: 540px;
      height: 1080px;
      background: linear-gradient(160deg, #FFFBF0 0%, #FFF0E6 100%);
      display: flex;
      flex-direction: column;
      justify-content: center;
      padding: 0 48px;
    }}

    /* 브랜드 라벨 — 40px */
    .brand-label {{
      font-size: 40px;
      font-weight: 600;
      color: #E8600A;
      line-height: 1.3;
      letter-spacing: -0.8px;
      margin-bottom: 28px;
      word-break: keep-all;
    }}

    /* "최대" 레이블 — 44px */
    .sub-label {{
      font-size: 44px;
      font-weight: 500;
      color: #5A6A7A;
      line-height: 1.2;
      letter-spacing: -0.5px;
      margin-bottom: 4px;
      word-break: keep-all;
    }}

    /* "1,000만원" 핵심 메시지 — 84px */
    .main-value {{
      font-size: 84px;
      font-weight: 900;
      color: #E8600A;
      line-height: 1.1;
      letter-spacing: -3px;
      margin-bottom: 28px;
      word-break: keep-all;
    }}

    /* "경력직 지원" — 44px */
    .career-label {{
      font-size: 44px;
      font-weight: 500;
      color: #5A6A7A;
      line-height: 1.2;
      letter-spacing: -0.5px;
      margin-bottom: 4px;
      word-break: keep-all;
    }}

    /* "직전연봉 50%까지" 보조 메시지 — 64px */
    .salary-line {{
      font-size: 64px;
      font-weight: 800;
      color: #1A2E4A;
      line-height: 1.15;
      letter-spacing: -2px;
      margin-bottom: 28px;
      word-break: keep-all;
    }}

    /* "2026년 7월 변경" — 44px */
    .deadline-label {{
      font-size: 44px;
      font-weight: 600;
      color: #C8860A;
      line-height: 1.2;
      letter-spacing: -0.5px;
      margin-bottom: 28px;
      word-break: keep-all;
    }}

    /* CTA 버튼 — 높이 64px, 텍스트 40px */
    .cta-button {{
      display: flex;
      align-items: center;
      justify-content: center;
      background: #E8600A;
      color: #FFFFFF;
      font-size: 40px;
      font-weight: 700;
      height: 64px;
      border-radius: 8px;
      letter-spacing: -0.5px;
      margin-bottom: 16px;
      word-break: keep-all;
      white-space: nowrap;
    }}

    /* 하단 조직 레이블 — 40px */
    .org-label {{
      font-size: 40px;
      font-weight: 400;
      color: #8A9AAA;
      text-align: center;
      line-height: 1.3;
      letter-spacing: -0.3px;
      word-break: keep-all;
    }}
  </style>
</head>
<body>
  <div class="container">

    <!-- 좌측 패널: 인물 사진 + 비네트 + 문제 제기 텍스트 -->
    <div class="left-panel">
      <img src="file://{bg_path}" alt="Korean businessman contemplating">
      <div class="left-vignette"></div>
      <div class="left-text">
        <span class="left-line">열심히는 하는데</span>
        <span class="left-line">소득은</span>
        <span class="left-line">제자리걸음?</span>
      </div>
    </div>

    <!-- 우측 패널: 따뜻한 크림 배경 + 해결책 텍스트 -->
    <div class="right-panel">

      <!-- 브랜드 라벨 (40px) -->
      <div class="brand-label">T.O.P 정착지원금</div>

      <!-- "최대" (44px) -->
      <div class="sub-label">최대</div>

      <!-- "1,000만원" — 핵심 메시지 (84px) -->
      <div class="main-value">1,000만원</div>

      <!-- "경력직 지원" (44px) -->
      <div class="career-label">경력직 지원</div>

      <!-- "직전연봉 50%까지" — 보조 메시지 (64px) -->
      <div class="salary-line">직전연봉 50%까지</div>

      <!-- "2026년 7월 변경" (44px) -->
      <div class="deadline-label">2026년 7월 변경</div>

      <!-- CTA 버튼 (높이 64px, 텍스트 40px) -->
      <div class="cta-button">상담 신청하기 →</div>

      <!-- 조직 레이블 (40px) -->
      <div class="org-label">T.O.P 사업단 | 인카금융서비스</div>
    </div>

  </div>
</body>
</html>
c                    t        d       t        j                  t        | j	                                     }t
        j                  |d       t        dt
                t
        S )uQ   배경 이미지 경로를 주입한 HTML 템플릿 v2 파일을 생성합니다.u2   [2/3] HTML 오버레이 템플릿 v2 생성 중...)bg_pathzutf-8)encodingu         템플릿 저장: )r'   HTML_TEMPLATEformatr$   resolveTEMPLATE_V2_PATH
write_text)rE   html_contents     r   create_html_templaterM   f  sU    	
>? ''GOO4E0F'GLw?	$%5$6
78    c                    t        d       |j                  j                  dd       t               5 }|j                  j                         }	 |j                  ddd      }d| j                          }|j                  |d	       |j                  d
       |j                  t        |      d       t        d|        |j                          	 ddd       y# |j                          w xY w# 1 sw Y   yxY w)uW   Playwright 헤드리스 브라우저로 HTML을 렌더링하여 PNG로 저장합니다.u2   [3/3] Playwright로 최종 이미지 캡처 중...Tparentsexist_oki8  )widthheight)viewportzfile://networkidle)
wait_untili	  png)r"   typeu         최종 이미지 저장: N)r'   parentmkdirr   chromiumlaunchnew_pagerI   gotowait_for_timeout
screenshotr$   close)template_pathoutput_pathr   browserpagetemplate_urls         r   capture_final_imagerh   s  s    	
>?TD9		 a**##%		##tt-L#MD$]%:%:%<$=>LIIl}I=!!$'OO[!1O>3K=ABMMO  MMO s$   C4A6CC4C11C44C=c                 ,   t        d       t        d       t        d       t        d       t        j                         } t        j                  dd       dD ]+  }t        |z  }|j	                         st        d| d       - t               }t        |      }t        |t               t        d	t        j                   d
t        j                          t        j                  t        t              t        t                     t        dt                t        j                         | z
  }t        j                         j                  dz  }t                t        d       t        d|dd       t        d|        t        d|        t        dt         d|dd       t        dt                t                t        d       t        d       t        d       t        d       t        d       t        d       t        d       y )Nz<============================================================u5   Concept #20 v2 — Split-Layout Solution 리메이크u8   폰트 규칙: 핵심 84px+ / 보조 64px+ / 최소 40pxTrP   )z
sample.pngzbg.pngztemplate.htmlu         [보호] u    유지 — 덮어쓰지 않음u	   [복사] u    → u         복사 완료: i   u   완료! 총 소요시간: z.1fu   초u   배경 이미지:  u   HTML 템플릿: u   최종 출력 1: r   z.0fz KB)u   최종 출력 2: u   [폰트 크기 검증]u*     핵심 메시지 '1,000만원': 84px ✓u5     보조 메시지 '직전연봉 50%까지': 64px ✓uP     보조 메시지 '열심히는 하는데/소득은/제자리걸음?': 64px ✓u'     CTA/브랜드/조직 라벨: 40px ✓u&     최소 기준 40px 미만 없음 ✓)r'   time
OUTPUT_DIRr[   existsrC   rM   rh   FINAL_OUTPUTnameCOPY_OUTPUTshutilcopy2r$   statst_size)
start_time	protectedr   rE   rc   elapsedsize_kbs          r   mainrx     s   	(O	
AB	
DE	(OJTD1 ? P	"88:OI;.MNOP "#G )1M |4 
Il''(k.>.>-?
@A
LL\"C$45	!+
/0iikJ&G!))D0G	G	(O	&wsm3
78	y
)*	]O
,-	l^2gc]$
?@	k]
+,	G	
"#	
67	
AB	
\]	
34	
23	(OrN   __main__)returnr   )rE   r   rz   r   )rc   r   rd   r   rz   None)rz   r{   )__doc__
__future__r   r4   rp   r!   rj   pathlibr   r-   playwright.sync_apir   r%   rk   r6   rJ   rm   ro   r+   r)   r,   rC   rG   rM   rh   rx   __name__r   rN   r   <module>r      s    #   
    / 89WX
+%
 22 O+33DDe 
7Y~up0/d zF rN   