refactor: 데모 브랜치 패키지 통일 com.example.medilightv2android → com.medithings.vesiscan
사용자앱 (feature/tab-navigation) 과 패키지 이름 일치. namespace + applicationId 둘 다 com.medithings.vesiscan 로 변경 (기존 데모 앱은 재설치 필요). ## 변경 범위 - Kotlin 148 파일: package + import 문 (714 occurrences) - 디렉토리 이동: com/example/medilightv2android → com/medithings/vesiscan (main, test, androidTest 각각) - app/build.gradle.kts: namespace, applicationId - docs/FLAVOR_DEMO_STABLE.md: 참조 갱신 - V41DetectorCH4Test: BvDispatchResult.methodChosen → method (dto field name fix) ## 주의 - applicationId 가 바뀌므로 기존 데모 앱 (com.example.medilightv2android.demo) 은 Android 관점에서 다른 앱으로 취급 — 재설치 시 PIN/설정 초기화됨. - Fresh install 권장. 기존 앱 (com.example...) 은 별도로 uninstall 필요. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
"""Extract exact visual design info from the reference PPTX into _design_dump.txt."""
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
from pptx import Presentation
|
||||
from pptx.util import Emu
|
||||
from pptx.dml.color import RGBColor
|
||||
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
||||
from pptx.oxml.ns import qn
|
||||
|
||||
PPT_DIR = Path(__file__).parent
|
||||
SRC = PPT_DIR / "주간보고서_dwjang_20260604.pptx"
|
||||
OUT = PPT_DIR / "_design_dump.txt"
|
||||
|
||||
|
||||
def emu_to_in(v):
|
||||
try:
|
||||
return f"{Emu(v).inches:.2f}"
|
||||
except Exception:
|
||||
return "?"
|
||||
|
||||
|
||||
def fmt_xy(left, top, width, height):
|
||||
return (
|
||||
f"L={left} T={top} W={width} H={height} EMU | "
|
||||
f"L={emu_to_in(left)}in T={emu_to_in(top)}in "
|
||||
f"W={emu_to_in(width)}in H={emu_to_in(height)}in"
|
||||
)
|
||||
|
||||
|
||||
def color_from_fill(fill):
|
||||
"""Best-effort extraction of solid fill color as hex."""
|
||||
try:
|
||||
if fill.type is None:
|
||||
return "none/inherit"
|
||||
# 1 = SOLID per MSO_FILL_TYPE
|
||||
if int(fill.type) == 1:
|
||||
fc = fill.fore_color
|
||||
try:
|
||||
rgb = fc.rgb
|
||||
if rgb is not None:
|
||||
return f"#{str(rgb)}"
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return f"theme:{fc.theme_color}"
|
||||
except Exception:
|
||||
pass
|
||||
return f"type={fill.type}"
|
||||
except Exception as e:
|
||||
return f"err:{e.__class__.__name__}"
|
||||
|
||||
|
||||
def color_from_line(line):
|
||||
try:
|
||||
c = line.color
|
||||
try:
|
||||
rgb = c.rgb
|
||||
if rgb is not None:
|
||||
return f"#{str(rgb)}"
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return f"theme:{c.theme_color}"
|
||||
except Exception:
|
||||
return "inherit"
|
||||
except Exception:
|
||||
return "n/a"
|
||||
|
||||
|
||||
def run_color(run):
|
||||
try:
|
||||
c = run.font.color
|
||||
try:
|
||||
rgb = c.rgb
|
||||
if rgb is not None:
|
||||
return f"#{str(rgb)}"
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return f"theme:{c.theme_color}"
|
||||
except Exception:
|
||||
return "inherit"
|
||||
except Exception:
|
||||
return "inherit"
|
||||
|
||||
|
||||
def slide_bg(slide):
|
||||
"""Try to read slide background fill."""
|
||||
try:
|
||||
bg = slide.background
|
||||
return color_from_fill(bg.fill)
|
||||
except Exception as e:
|
||||
return f"err:{e.__class__.__name__}"
|
||||
|
||||
|
||||
def shape_type_name(shape):
|
||||
try:
|
||||
st = shape.shape_type
|
||||
if st is None:
|
||||
return "None"
|
||||
return str(st)
|
||||
except Exception:
|
||||
return "?"
|
||||
|
||||
|
||||
def autoshape_kind(shape):
|
||||
try:
|
||||
return str(shape.auto_shape_type)
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def dump_text_frame(tf, out_lines, indent=" "):
|
||||
for pi, para in enumerate(tf.paragraphs):
|
||||
lvl = para.level
|
||||
align = para.alignment
|
||||
# Detect bullet via pPr/buChar/buAutoNum
|
||||
pPr = para._pPr
|
||||
bullet = "-"
|
||||
if pPr is not None:
|
||||
if pPr.find(qn("a:buNone")) is not None:
|
||||
bullet = "noBul"
|
||||
elif pPr.find(qn("a:buChar")) is not None:
|
||||
bc = pPr.find(qn("a:buChar"))
|
||||
bullet = f"buChar={bc.get('char')!r}"
|
||||
elif pPr.find(qn("a:buAutoNum")) is not None:
|
||||
bullet = "buAutoNum"
|
||||
runs_info = []
|
||||
full_txt = ""
|
||||
for r in para.runs:
|
||||
t = r.text or ""
|
||||
full_txt += t
|
||||
f = r.font
|
||||
runs_info.append({
|
||||
"text": t,
|
||||
"name": f.name,
|
||||
"size": (f.size.pt if f.size else None),
|
||||
"bold": f.bold,
|
||||
"italic": f.italic,
|
||||
"color": run_color(r),
|
||||
})
|
||||
snippet = full_txt[:60].replace("\n", " ")
|
||||
out_lines.append(
|
||||
f"{indent}p{pi} lvl={lvl} align={align} {bullet} text={snippet!r}"
|
||||
)
|
||||
for ri, info in enumerate(runs_info):
|
||||
tt = info["text"][:40].replace("\n", " ")
|
||||
out_lines.append(
|
||||
f"{indent} r{ri} font={info['name']!r} sz={info['size']} "
|
||||
f"b={info['bold']} i={info['italic']} c={info['color']} t={tt!r}"
|
||||
)
|
||||
|
||||
|
||||
def dump_shape(shape, out_lines, indent=" "):
|
||||
name = shape.name
|
||||
st = shape_type_name(shape)
|
||||
kind = autoshape_kind(shape) if st == "MSO_SHAPE_TYPE.AUTO_SHAPE (1)" else ""
|
||||
try:
|
||||
left, top, width, height = shape.left, shape.top, shape.width, shape.height
|
||||
except Exception:
|
||||
left = top = width = height = None
|
||||
out_lines.append(
|
||||
f"{indent}- shape name={name!r} type={st} autoshape={kind}"
|
||||
)
|
||||
out_lines.append(f"{indent} geom: {fmt_xy(left, top, width, height)}")
|
||||
|
||||
# Fill
|
||||
try:
|
||||
if shape.has_text_frame or st.startswith("MSO_SHAPE_TYPE.AUTO_SHAPE") or st == "MSO_SHAPE_TYPE.TEXT_BOX (17)":
|
||||
fill_c = color_from_fill(shape.fill)
|
||||
out_lines.append(f"{indent} fill: {fill_c}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Line
|
||||
try:
|
||||
line = shape.line
|
||||
lc = color_from_line(line)
|
||||
try:
|
||||
lw = line.width
|
||||
lw_pt = (lw / 12700) if lw else None
|
||||
except Exception:
|
||||
lw_pt = None
|
||||
out_lines.append(f"{indent} line: color={lc} width_pt={lw_pt}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Picture
|
||||
if st == "MSO_SHAPE_TYPE.PICTURE (13)":
|
||||
try:
|
||||
img = shape.image
|
||||
out_lines.append(
|
||||
f"{indent} picture: ext={img.ext} sha1={img.sha1[:10]} "
|
||||
f"size_bytes={len(img.blob)} filename={getattr(img, 'filename', None)!r}"
|
||||
)
|
||||
except Exception as e:
|
||||
out_lines.append(f"{indent} picture: err={e}")
|
||||
|
||||
# Text
|
||||
if shape.has_text_frame:
|
||||
tf = shape.text_frame
|
||||
# vertical anchor
|
||||
try:
|
||||
va = tf.vertical_anchor
|
||||
except Exception:
|
||||
va = None
|
||||
try:
|
||||
wrap = tf.word_wrap
|
||||
except Exception:
|
||||
wrap = None
|
||||
out_lines.append(
|
||||
f"{indent} textframe: anchor={va} wrap={wrap} "
|
||||
f"mL={tf.margin_left} mR={tf.margin_right} mT={tf.margin_top} mB={tf.margin_bottom}"
|
||||
)
|
||||
# Full text first 60 chars
|
||||
full = (shape.text_frame.text or "").replace("\n", " | ")
|
||||
out_lines.append(f"{indent} text(60)={full[:60]!r}")
|
||||
dump_text_frame(tf, out_lines, indent=indent + " ")
|
||||
|
||||
# Group
|
||||
if st == "MSO_SHAPE_TYPE.GROUP (6)":
|
||||
out_lines.append(f"{indent} group children:")
|
||||
for child in shape.shapes:
|
||||
dump_shape(child, out_lines, indent=indent + " ")
|
||||
|
||||
|
||||
def main():
|
||||
prs = Presentation(SRC)
|
||||
lines = []
|
||||
lines.append(f"FILE: {SRC.name}")
|
||||
lines.append(
|
||||
f"SLIDE SIZE: W={prs.slide_width} H={prs.slide_height} EMU | "
|
||||
f"W={emu_to_in(prs.slide_width)}in H={emu_to_in(prs.slide_height)}in"
|
||||
)
|
||||
lines.append(f"SLIDE COUNT: {len(prs.slides)}")
|
||||
lines.append("")
|
||||
# Layouts available
|
||||
lines.append("LAYOUTS:")
|
||||
for i, lay in enumerate(prs.slide_layouts):
|
||||
lines.append(f" [{i}] {lay.name}")
|
||||
lines.append("")
|
||||
# Master fonts / theme colors (best effort)
|
||||
try:
|
||||
theme_xml = prs.slide_masters[0].element.getroottree()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for si, slide in enumerate(prs.slides, start=1):
|
||||
lines.append("=" * 70)
|
||||
lines.append(f"SLIDE {si}")
|
||||
lines.append(f" layout: {slide.slide_layout.name}")
|
||||
lines.append(f" background: {slide_bg(slide)}")
|
||||
lines.append(f" shape_count: {len(slide.shapes)}")
|
||||
for shp in slide.shapes:
|
||||
dump_shape(shp, lines, indent=" ")
|
||||
lines.append("")
|
||||
|
||||
OUT.write_text("\n".join(lines), encoding="utf-8")
|
||||
print(f"WROTE: {OUT} lines={len(lines)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user