Compare commits
	
		
			2 Commits 
		
	
	
		
			91be974dbc
			...
			9816ee7f6d
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						9816ee7f6d | |
| 
							
							
								
								 | 
						697f7ace59 | 
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										9
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										9
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
build:
 | 
					build:
 | 
				
			||||||
	python3 build.py
 | 
						@mkdir -p out
 | 
				
			||||||
	py3clean .
 | 
						@python3 build.py -B  2> out/err.log
 | 
				
			||||||
 | 
						@py3clean .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install: build
 | 
					install: build
 | 
				
			||||||
	cp out/KawaiiMonoRegularPatched.ttf ~/.local/share/fonts/KawaiiMonoRegular.ttf
 | 
						@cp out/KawaiiMonoRegularPatched.ttf ~/.local/share/fonts/KawaiiMonoRegular.ttf
 | 
				
			||||||
	fc-cache -f -v
 | 
						@fc-cache -f -v
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								build.py
								
								
								
								
							
							
						
						
									
										4
									
								
								build.py
								
								
								
								
							| 
						 | 
					@ -1,3 +1,7 @@
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/src"))
 | 
				
			||||||
 | 
					sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/assets"))
 | 
				
			||||||
from src.build import build
 | 
					from src.build import build
 | 
				
			||||||
import config
 | 
					import config
 | 
				
			||||||
if __name__ == "__main__": build(config.config)
 | 
					if __name__ == "__main__": build(config.config)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								config.py
								
								
								
								
							
							
						
						
									
										18
									
								
								config.py
								
								
								
								
							| 
						 | 
					@ -41,25 +41,31 @@ config = {
 | 
				
			||||||
    # 폰트 용량이 매우 커집니다!!
 | 
					    # 폰트 용량이 매우 커집니다!!
 | 
				
			||||||
    "CopyKoreanGlyphs": True,
 | 
					    "CopyKoreanGlyphs": True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: 이 설정은 아직 동작하지 않습니다
 | 
					 | 
				
			||||||
    # 노토 산스 모노에서 가타카나/히라가나
 | 
					    # 노토 산스 모노에서 가타카나/히라가나
 | 
				
			||||||
    # 글리프를 복사할 지에 대한 여부입니다
 | 
					    # 글리프를 복사할 지에 대한 여부입니다
 | 
				
			||||||
    # 한자는 포함하지 않습니다
 | 
					    # 한자는 포함하지 않습니다
 | 
				
			||||||
    "CopyJapaneseGlyphs": False,
 | 
					    "CopyJapaneseGlyphs": True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 노토 산스에서 단위관련 기호, 원형 기호
 | 
				
			||||||
 | 
					    # 글리프를 복사할 지에 대한 여부입니다
 | 
				
			||||||
 | 
					    # 폰트위 용량이 매우 커집니다!!
 | 
				
			||||||
 | 
					    "CopySymbols": True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: 이 설정은 아직 동작하지 않습니다
 | 
					 | 
				
			||||||
    # 노토 산스 모노에서 CJK 공용 한자 글리프를
 | 
					    # 노토 산스 모노에서 CJK 공용 한자 글리프를
 | 
				
			||||||
    # 복사할 지에 대한 여부입니다.
 | 
					    # 복사할 지에 대한 여부입니다.
 | 
				
			||||||
    # 폰트 용량이 매우 커집니다!!
 | 
					    # 폰트 용량이 매우 커집니다!!
 | 
				
			||||||
    # 웹용 폰트의 경우 끄는것을 추천합니다
 | 
					    # 웹용 폰트의 경우 끄는것을 추천합니다
 | 
				
			||||||
    "CopyCJKUnifiedIdeographs": False,
 | 
					    #! 최소 2분 이상 걸립니다!!
 | 
				
			||||||
 | 
					    "CopyCJKUnifiedIdeographs": True, # 일반적인 CJK Unified
 | 
				
			||||||
 | 
					    "CopyCJKUnifiedIdeographsExtension": False, # Extension A~F
 | 
				
			||||||
 | 
					    "CopyCJKCompatibilityIdeographs": False, # Compatibility Ideograph, Supplement
 | 
				
			||||||
 | 
					    #! 이 옵션들을 활성화시 ttf 포멧으로 저장에 실패할 수 있습니다
 | 
				
			||||||
 | 
					    #? ttf 의 글자수 제한 때문에 그런것이므로, 다른 포멧으로 저장해야합니다.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: 이 설정은 아직 동작하지 않습니다
 | 
					 | 
				
			||||||
    # 라틴 글리프를 Hack 폰트에서 더 가져옵니다
 | 
					    # 라틴 글리프를 Hack 폰트에서 더 가져옵니다
 | 
				
			||||||
    # (성조 표시된 라틴, ...)
 | 
					    # (성조 표시된 라틴, ...)
 | 
				
			||||||
    "CopyLatinExtra": True,
 | 
					    "CopyLatinExtra": True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: 이 설정은 아직 동작하지 않습니다
 | 
					 | 
				
			||||||
    # Nerd Fonts 패치를 적용할지에 대한
 | 
					    # Nerd Fonts 패치를 적용할지에 대한
 | 
				
			||||||
    # 여부입니다. 폰트 용량이 매우 커집니다!!
 | 
					    # 여부입니다. 폰트 용량이 매우 커집니다!!
 | 
				
			||||||
    # 웹용 폰트의 경우 끄는것을 추천합니다
 | 
					    # 웹용 폰트의 경우 끄는것을 추천합니다
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,87 +0,0 @@
 | 
				
			||||||
from . import wgetHandler
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import zipfile
 | 
					 | 
				
			||||||
import shutil
 | 
					 | 
				
			||||||
import math
 | 
					 | 
				
			||||||
from . import utility as Utility
 | 
					 | 
				
			||||||
import fontforge
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
link_NanumSquareNeo = "https://campaign.naver.com/nanumsquare_neo/download/NaverNanumSquareNeo.zip"
 | 
					 | 
				
			||||||
patchVersion = 2 # 업데이트 후 캐시를 무시하기 위해서 사용
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 폰트 다운로드와 열기
 | 
					 | 
				
			||||||
# 배포 방식이 zip 으로 배포이기 때문에 zipfile 라이브러리로
 | 
					 | 
				
			||||||
# 다운로드 후 언팩함
 | 
					 | 
				
			||||||
def getFontPath():
 | 
					 | 
				
			||||||
    if not os.path.exists("assets"): os.mkdir("assets")
 | 
					 | 
				
			||||||
    if not os.path.exists("assets/NanumSquareNeoKr.ttf"):
 | 
					 | 
				
			||||||
        if not os.path.exists("assets/NanumSquareNeoKr.zip"):
 | 
					 | 
				
			||||||
            wgetHandler.download(link_NanumSquareNeo,"assets/NanumSquareNeoKr.zip")
 | 
					 | 
				
			||||||
        print("Unzipping NanumSquareNeoKr.zip",end="")
 | 
					 | 
				
			||||||
        with zipfile.ZipFile("assets/NanumSquareNeoKr.zip", 'r') as zip_ref:
 | 
					 | 
				
			||||||
            extractName = zip_ref.extract("NaverNanumSquareNeo/TTF/NanumSquareNeo-bRg.ttf","assets/NanumSquareNeoKr.extract")
 | 
					 | 
				
			||||||
            os.rename(extractName,"assets/NanumSquareNeoKr.ttf")
 | 
					 | 
				
			||||||
            shutil.rmtree('assets/NanumSquareNeoKr.extract')
 | 
					 | 
				
			||||||
        print(" [OK]")
 | 
					 | 
				
			||||||
    return "assets/NanumSquareNeoKr.ttf"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 한글 범위의 글립을 선택함
 | 
					 | 
				
			||||||
def selectGlyphs(font):
 | 
					 | 
				
			||||||
    font.selection.none()
 | 
					 | 
				
			||||||
    font.selection.select(("more","ranges","unicode"),0x3131,0x32BF) # ㄱ ~ ㊿
 | 
					 | 
				
			||||||
    font.selection.select(("more","ranges","unicode"),0xAC00,0xD7A3) # 가 ~ 힣
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 굵기/폭 설정 캐시파일 만들기
 | 
					 | 
				
			||||||
def getCache(sourcePath,baseSize=550,weight=16):
 | 
					 | 
				
			||||||
    # 캐시된 파일을 확인하고 있으면 반환
 | 
					 | 
				
			||||||
    filename = "assets/cache/NanumSquareNeoKr.cache_{}.base_{}.weight_{}.sfd".format(patchVersion,baseSize,weight)
 | 
					 | 
				
			||||||
    if os.path.exists(filename):
 | 
					 | 
				
			||||||
        return fontforge.open(filename)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 새로운 캐시용 폰트 생성
 | 
					 | 
				
			||||||
    cache=fontforge.font()
 | 
					 | 
				
			||||||
    cache.encoding = 'UnicodeFull'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 소스 폰트를 패치시킴
 | 
					 | 
				
			||||||
    source=fontforge.open(sourcePath)
 | 
					 | 
				
			||||||
    selectGlyphs(source)
 | 
					 | 
				
			||||||
    source.changeWeight(weight) # 굵기 변경
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 너비 지정
 | 
					 | 
				
			||||||
    Utility.setWidthWithSavingPosition(
 | 
					 | 
				
			||||||
        font=source,targetWidth=baseSize*2
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 캐시에 붇여넣기
 | 
					 | 
				
			||||||
    source.copy()
 | 
					 | 
				
			||||||
    selectGlyphs(cache)
 | 
					 | 
				
			||||||
    cache.paste()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 캐시 폰트 저장
 | 
					 | 
				
			||||||
    if not os.path.exists("assets/cache"): os.mkdir("assets/cache")
 | 
					 | 
				
			||||||
    cache.save(filename)
 | 
					 | 
				
			||||||
    return cache
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular 같은 문자열 weight 를 포인트 값으로 변경
 | 
					 | 
				
			||||||
weightStrToNum = {
 | 
					 | 
				
			||||||
    "Regular": 16,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 캐시를 가져와서 글리프를 타겟 폰트에 붇여넣음
 | 
					 | 
				
			||||||
def pasteGlyphs(target,sourcePath,baseSize=550,weightStr="Regular"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 캐시된 소스를 읽어드림
 | 
					 | 
				
			||||||
    source = getCache(
 | 
					 | 
				
			||||||
        sourcePath = sourcePath,
 | 
					 | 
				
			||||||
        baseSize = baseSize,
 | 
					 | 
				
			||||||
        weight = weightStrToNum.get(weightStr)
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 타겟으로 글리프 복사
 | 
					 | 
				
			||||||
    selectGlyphs(source)
 | 
					 | 
				
			||||||
    source.copy()
 | 
					 | 
				
			||||||
    selectGlyphs(target)
 | 
					 | 
				
			||||||
    target.paste()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 캐시 닫기
 | 
					 | 
				
			||||||
    source.close()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
from . import wgetHandler
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
github_NotoSansMonoCJKkr = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/Mono/NotoSansMonoCJKkr-Regular.otf"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 폰트 다운로드와 열기
 | 
					 | 
				
			||||||
def getFontPath():
 | 
					 | 
				
			||||||
    if not os.path.exists("assets"): os.mkdir("assets")
 | 
					 | 
				
			||||||
    if not os.path.exists("assets/NotoMonoCJKkr.otf"):
 | 
					 | 
				
			||||||
        wgetHandler.download(github_NotoSansMonoCJKkr,"assets/NotoMonoCJKkr.otf")
 | 
					 | 
				
			||||||
    return "assets/NotoMonoCJKkr.otf"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def pasteGlyphs(target,source,baseSize=550,JapaneseGlyphs=False,CJKUnifiedIdeographs=False):
 | 
					 | 
				
			||||||
    source.cidFlatten()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def select(font):
 | 
					 | 
				
			||||||
        font.selection.none()
 | 
					 | 
				
			||||||
        font.selection.select(("more","ranges","unicode"),0x3131,0x32BF) # ㄱ ~ ㊿
 | 
					 | 
				
			||||||
        font.selection.select(("more","ranges","unicode"),0xAC00,0xD7A3) # 가 ~ 힣
 | 
					 | 
				
			||||||
    select(source)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 넓은 글자 크기 (한글에 모두 적용)
 | 
					 | 
				
			||||||
    wideWidth = baseSize*2
 | 
					 | 
				
			||||||
    for glyph in source.selection.byGlyphs:
 | 
					 | 
				
			||||||
        widthDiff = wideWidth-glyph.width # 타겟 너비와 얼마나 크기 차이가 나는지
 | 
					 | 
				
			||||||
        sideAdjust = widthDiff/2 # 좌우 사이드 조정해야하는 정도
 | 
					 | 
				
			||||||
        glyph.left_side_bearing = int(glyph.left_side_bearing + math.floor(sideAdjust)) # 좌우 베어링을 조정함
 | 
					 | 
				
			||||||
        glyph.right_side_bearing = int(glyph.right_side_bearing + math.ceil(sideAdjust))
 | 
					 | 
				
			||||||
        glyph.width = wideWidth # 타겟 너비로 정확하게 설정
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 타겟으로 글리프 복사
 | 
					 | 
				
			||||||
    source.copy()
 | 
					 | 
				
			||||||
    select(target)
 | 
					 | 
				
			||||||
    target.paste()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    getFontPath()
 | 
					 | 
				
			||||||
							
								
								
									
										58
									
								
								src/build.py
								
								
								
								
							
							
						
						
									
										58
									
								
								src/build.py
								
								
								
								
							| 
						 | 
					@ -1,58 +0,0 @@
 | 
				
			||||||
import fontforge
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from . import NanumSquareNeo as NanumSquareNeoLoader
 | 
					 | 
				
			||||||
from . import NotoMono as NotoMonoLoader
 | 
					 | 
				
			||||||
from . import KawaiiMono as KawaiiMonoLoader
 | 
					 | 
				
			||||||
from . import utility as Utility
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def build(config=None):
 | 
					 | 
				
			||||||
    # 메인 폰트 불러오기
 | 
					 | 
				
			||||||
    kawaii = fontforge.open(
 | 
					 | 
				
			||||||
        KawaiiMonoLoader.getFontPath())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 모든 글리프를 붇여넣을 수 있도록 인코딩을 utf full 로 변경
 | 
					 | 
				
			||||||
    kawaii.encoding = 'UnicodeFull'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 폰트 가로폭 설정
 | 
					 | 
				
			||||||
    baseSize = config.get("FontBaseWidth")
 | 
					 | 
				
			||||||
    if baseSize != 550:
 | 
					 | 
				
			||||||
        Utility.setWidthWithSavingPosition(
 | 
					 | 
				
			||||||
            font=kawaii,targetWidth=baseSize
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 한글 글리프 붇여넣기
 | 
					 | 
				
			||||||
    if config.get("CopyKoreanGlyphs"):
 | 
					 | 
				
			||||||
        # 나눔 스퀘어 네오 다운로드/불러오기
 | 
					 | 
				
			||||||
        nanumSquareNeo = NanumSquareNeoLoader.getFontPath()
 | 
					 | 
				
			||||||
        # 글리프 붇여넣기
 | 
					 | 
				
			||||||
        NanumSquareNeoLoader.pasteGlyphs(
 | 
					 | 
				
			||||||
            target=kawaii,baseSize=baseSize,weightStr="Regular",
 | 
					 | 
				
			||||||
            sourcePath=nanumSquareNeo)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (config.get("CopyJapaneseGlyphs") or
 | 
					 | 
				
			||||||
        config.get("CopyCJKUnifiedIdeographs")):
 | 
					 | 
				
			||||||
        # 노토 모노 다운로드/불러오기
 | 
					 | 
				
			||||||
        notoMono = fontforge.open(
 | 
					 | 
				
			||||||
            NotoMonoLoader.getFontPath())
 | 
					 | 
				
			||||||
        # 글리프 붇여넣기
 | 
					 | 
				
			||||||
        NotoMonoLoader.pasteGlyphs(
 | 
					 | 
				
			||||||
            JapaneseGlyphs=config.get("CopyJapaneseGlyphs") or False,
 | 
					 | 
				
			||||||
            CJKUnifiedIdeographs=config.get("CopyCJKUnifiedIdeographs") or False,
 | 
					 | 
				
			||||||
            target=kawaii,baseSize=550,
 | 
					 | 
				
			||||||
            source=notoMono)
 | 
					 | 
				
			||||||
        notoMono.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 생성
 | 
					 | 
				
			||||||
    if not os.path.exists("out"): os.mkdir("out")
 | 
					 | 
				
			||||||
    kawaii.generate("out/"+"KawaiiMonoRegularPatched.ttf")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # KawaiiMonoRegularPatched.ttf
 | 
					 | 
				
			||||||
    # KawaiiMonoRegularPatched.otf
 | 
					 | 
				
			||||||
    # KawaiiMonoRegularPatched.woff
 | 
					 | 
				
			||||||
    # KawaiiMonoRegularPatched.eot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 파일 닫기
 | 
					 | 
				
			||||||
    kawaii.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__": build()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					patchVersion = 2 # 업데이트 후 캐시를 무시하기 위해서 사용
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import select as selectGlyphs
 | 
				
			||||||
 | 
					from .download import download
 | 
				
			||||||
 | 
					from .cacheBuilder import getCachedFont
 | 
				
			||||||
 | 
					from .patcher import pasteGlyphs
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import utility as Utility
 | 
				
			||||||
 | 
					import fontforge
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from . import selectGlyphs
 | 
				
			||||||
 | 
					from . import patchVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 굵기/폭 설정 캐시파일 만들기
 | 
				
			||||||
 | 
					def getCachedFont(sourcePath,baseSize=550,weight=16):
 | 
				
			||||||
 | 
					    # 캐시된 파일을 확인하고 있으면 반환
 | 
				
			||||||
 | 
					    filename = "assets/cache/NanumSquareNeoKr.cache_{}.base_{}.weight_{}.sfd".format(patchVersion,baseSize,weight)
 | 
				
			||||||
 | 
					    if os.path.exists(filename):
 | 
				
			||||||
 | 
					        print("Found build cache [OK]")
 | 
				
			||||||
 | 
					        return fontforge.open(filename)
 | 
				
			||||||
 | 
					    print("Creating new build cache",end="")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 새로운 캐시용 폰트 생성
 | 
				
			||||||
 | 
					    cache=fontforge.font()
 | 
				
			||||||
 | 
					    cache.encoding = 'UnicodeFull'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 소스 폰트를 패치시킴
 | 
				
			||||||
 | 
					    source=fontforge.open(sourcePath)
 | 
				
			||||||
 | 
					    selectGlyphs.Korean(source)
 | 
				
			||||||
 | 
					    source.changeWeight(weight) # 굵기 변경
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 너비 지정
 | 
				
			||||||
 | 
					    Utility.setWidthWithSavingPosition(
 | 
				
			||||||
 | 
					        font=source,targetWidth=baseSize*2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시에 붇여넣기
 | 
				
			||||||
 | 
					    source.copy()
 | 
				
			||||||
 | 
					    selectGlyphs.Korean(cache)
 | 
				
			||||||
 | 
					    cache.paste()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시 폰트 저장
 | 
				
			||||||
 | 
					    if not os.path.exists("assets/cache"): os.mkdir("assets/cache")
 | 
				
			||||||
 | 
					    cache.save(filename)
 | 
				
			||||||
 | 
					    print(" [OK]")
 | 
				
			||||||
 | 
					    return cache
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					import zipfile
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					import wgetHandler
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					link_NanumSquareNeo = "https://campaign.naver.com/nanumsquare_neo/download/NaverNanumSquareNeo.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 폰트 다운로드와 열기
 | 
				
			||||||
 | 
					# 배포 방식이 zip 으로 배포이기 때문에 zipfile 라이브러리로
 | 
				
			||||||
 | 
					# 다운로드 후 언팩함
 | 
				
			||||||
 | 
					def download():
 | 
				
			||||||
 | 
					    if not os.path.exists("assets"): os.mkdir("assets")
 | 
				
			||||||
 | 
					    if not os.path.exists("assets/NanumSquareNeoKr.ttf"):
 | 
				
			||||||
 | 
					        if not os.path.exists("assets/NanumSquareNeoKr.zip"):
 | 
				
			||||||
 | 
					            wgetHandler.download(link_NanumSquareNeo,"assets/NanumSquareNeoKr.zip")
 | 
				
			||||||
 | 
					        print("Unzipping NanumSquareNeoKr.zip",end="")
 | 
				
			||||||
 | 
					        with zipfile.ZipFile("assets/NanumSquareNeoKr.zip", 'r') as zip_ref:
 | 
				
			||||||
 | 
					            extractName = zip_ref.extract("NaverNanumSquareNeo/TTF/NanumSquareNeo-bRg.ttf","assets/NanumSquareNeoKr.extract")
 | 
				
			||||||
 | 
					            os.rename(extractName,"assets/NanumSquareNeoKr.ttf")
 | 
				
			||||||
 | 
					            shutil.rmtree('assets/NanumSquareNeoKr.extract')
 | 
				
			||||||
 | 
					        print(" [OK]")
 | 
				
			||||||
 | 
					    return "assets/NanumSquareNeoKr.ttf"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					from . import getCachedFont
 | 
				
			||||||
 | 
					from . import selectGlyphs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Regular 같은 문자열 weight 를 포인트 값으로 변경
 | 
				
			||||||
 | 
					weightStrToNum = {
 | 
				
			||||||
 | 
					    "Regular": 16,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 캐시를 가져와서 글리프를 타겟 폰트에 붇여넣음
 | 
				
			||||||
 | 
					def pasteGlyphs(target,sourcePath,deselectOriginalGlyphs,baseSize=550,weightStr="Regular"):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시된 소스를 읽어드림
 | 
				
			||||||
 | 
					    print("Patching: NanumSquareNeo")
 | 
				
			||||||
 | 
					    source = getCachedFont(
 | 
				
			||||||
 | 
					        sourcePath = sourcePath,
 | 
				
			||||||
 | 
					        baseSize = baseSize,
 | 
				
			||||||
 | 
					        weight = weightStrToNum.get(weightStr)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(source)
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 타겟으로 글리프 복사
 | 
				
			||||||
 | 
					    selectGlyphs.Korean(source)
 | 
				
			||||||
 | 
					    deselectOriginalGlyphs(source)
 | 
				
			||||||
 | 
					    source.copy()
 | 
				
			||||||
 | 
					    selectGlyphs.Korean(target)
 | 
				
			||||||
 | 
					    deselectOriginalGlyphs(target)
 | 
				
			||||||
 | 
					    target.paste()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시 닫기
 | 
				
			||||||
 | 
					    source.close()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					# 한글 범위의 글립을 선택함
 | 
				
			||||||
 | 
					def Korean(font):
 | 
				
			||||||
 | 
					    font.selection.none()
 | 
				
			||||||
 | 
					    font.selection.select(("more","ranges","unicode"),0x3131,0x32BF) # ㄱ ~ ㊿
 | 
				
			||||||
 | 
					    font.selection.select(("more","ranges","unicode"),0xAC00,0xD7A3) # 가 ~ 힣
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def Clear(font):
 | 
				
			||||||
 | 
					    font.selection.none()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .download import downloadPatcher
 | 
				
			||||||
 | 
					from .download import nerdFonts_Download_Test
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,6 @@
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					import wgetHandler
 | 
				
			||||||
import zipfile
 | 
					import zipfile
 | 
				
			||||||
# from .. import wgetHandler
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import importlib
 | 
					 | 
				
			||||||
basePath = os.path.realpath(os.path.dirname(__file__)+"/../")
 | 
					 | 
				
			||||||
wgetHandler = importlib.util.module_from_spec(importlib.util.spec_from_file_location("wgetHandler",basePath+"/wgetHandler.py"))
 | 
					 | 
				
			||||||
# import wgetHandler
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
print("__file__ : {__file__}".format(__file__=__file__))
 | 
					 | 
				
			||||||
print("wgetHandler (spec)")
 | 
					 | 
				
			||||||
print(importlib.util.spec_from_file_location("wgetHandler",basePath))
 | 
					 | 
				
			||||||
print("wgetHandler [object]")
 | 
					 | 
				
			||||||
print(wgetHandler)
 | 
					 | 
				
			||||||
print(help(wgetHandler))
 | 
					 | 
				
			||||||
# import wgetHandler
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
link_FontPatcher = "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/FontPatcher.zip"
 | 
					link_FontPatcher = "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/FontPatcher.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +12,10 @@ def downloadPatcher():
 | 
				
			||||||
        print("Unzipping NerdFontPatcher.zip",end="")
 | 
					        print("Unzipping NerdFontPatcher.zip",end="")
 | 
				
			||||||
        with zipfile.ZipFile("assets/NerdFontPatcher.zip", 'r') as zip_ref:
 | 
					        with zipfile.ZipFile("assets/NerdFontPatcher.zip", 'r') as zip_ref:
 | 
				
			||||||
            extractName = zip_ref.extractall("assets/NerdFontPatcher_extract")
 | 
					            extractName = zip_ref.extractall("assets/NerdFontPatcher_extract")
 | 
				
			||||||
 | 
					        os.rename("assets/NerdFontPatcher_extract/font-patcher","assets/NerdFontPatcher_extract/fontPatcher.py")
 | 
				
			||||||
 | 
					        with open("assets/NerdFontPatcher_extract/__init__.py","w") as file:
 | 
				
			||||||
 | 
					            file.write("")
 | 
				
			||||||
        print(" [OK]")
 | 
					        print(" [OK]")
 | 
				
			||||||
    return "assets/NanumSquareNeoKr.ttf"
 | 
					    return "assets/NerdFontPatcher_extract"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__": downloadPatcher()
 | 
					def nerdFonts_Download_Test(): downloadPatcher()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					patchVersion = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .download import download
 | 
				
			||||||
 | 
					from . import select as selectGlyphs
 | 
				
			||||||
 | 
					from .cacheBuilder import getCachedFont
 | 
				
			||||||
 | 
					from .patcher import pasteGlyphs
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import utility as Utility
 | 
				
			||||||
 | 
					import fontforge
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from . import selectGlyphs
 | 
				
			||||||
 | 
					from . import patchVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 굵기/폭 설정 캐시파일 만들기
 | 
				
			||||||
 | 
					def getCachedFont(sourcePath,EnabledItems,baseSize=550,weight=16):
 | 
				
			||||||
 | 
					    # 캐시된 파일을 확인하고 있으면 반환
 | 
				
			||||||
 | 
					    filename = "assets/cache/NotoMono.cache_{}.base_{}.weight_{}{}{}{}{}{}.sfd".format(
 | 
				
			||||||
 | 
					        patchVersion,baseSize,weight,
 | 
				
			||||||
 | 
					        EnabledItems.get("JapaneseGlyphs") and ".jp" or "",
 | 
				
			||||||
 | 
					        EnabledItems.get("CJKUnifiedIdeographs") and ".id" or "",
 | 
				
			||||||
 | 
					        EnabledItems.get("CopyCJKUnifiedIdeographsExtension") and ".ide" or "",
 | 
				
			||||||
 | 
					        EnabledItems.get("CopyCJKCompatibilityIdeographs") and ".cid" or "",
 | 
				
			||||||
 | 
					        EnabledItems.get("Symbols") and ".sym" or "")
 | 
				
			||||||
 | 
					    if os.path.exists(filename):
 | 
				
			||||||
 | 
					        print("Found build cache [OK]")
 | 
				
			||||||
 | 
					        return fontforge.open(filename)
 | 
				
			||||||
 | 
					    print("Creating new build cache",end="")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 새로운 캐시용 폰트 생성
 | 
				
			||||||
 | 
					    cache=fontforge.font()
 | 
				
			||||||
 | 
					    cache.encoding = 'UnicodeFull'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 소스 폰트를 패치시킴
 | 
				
			||||||
 | 
					    source=fontforge.open(sourcePath)
 | 
				
			||||||
 | 
					    source.cidFlatten()
 | 
				
			||||||
 | 
					    source.encoding = 'UnicodeFull'
 | 
				
			||||||
 | 
					    if EnabledItems.get("JapaneseGlyphs"): selectGlyphs.JapaneseGlyphs(source)
 | 
				
			||||||
 | 
					    if EnabledItems.get("Symbols"): selectGlyphs.Symbols(source)
 | 
				
			||||||
 | 
					    source.changeWeight(weight) # 굵기 변경
 | 
				
			||||||
 | 
					    selectGlyphs.SelectByEnabledList(source,EnabledItems)
 | 
				
			||||||
 | 
					    # 한자 글립은 냥많아서 크기조절하면 끝이 안난다냥
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 너비 지정
 | 
				
			||||||
 | 
					    Utility.setWidthWithSavingPosition(
 | 
				
			||||||
 | 
					        font=source,targetWidth=baseSize*2
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Utility.scale(font=source,targetScale=0.85)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시에 붇여넣기
 | 
				
			||||||
 | 
					    source.copy()
 | 
				
			||||||
 | 
					    selectGlyphs.SelectByEnabledList(cache,EnabledItems)
 | 
				
			||||||
 | 
					    cache.paste()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(cache)
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(source)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시 폰트 저장
 | 
				
			||||||
 | 
					    if not os.path.exists("assets/cache"): os.mkdir("assets/cache")
 | 
				
			||||||
 | 
					    cache.save(filename)
 | 
				
			||||||
 | 
					    print(" [OK]")
 | 
				
			||||||
 | 
					    return cache
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import wgetHandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					github_NotoSansMonoCJKkr = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/Mono/NotoSansMonoCJKkr-Regular.otf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 폰트 다운로드와 열기
 | 
				
			||||||
 | 
					def download():
 | 
				
			||||||
 | 
					    if not os.path.exists("assets"): os.mkdir("assets")
 | 
				
			||||||
 | 
					    if not os.path.exists("assets/NotoMonoCJKkr.otf"):
 | 
				
			||||||
 | 
					        wgetHandler.download(github_NotoSansMonoCJKkr,"assets/NotoMonoCJKkr.otf")
 | 
				
			||||||
 | 
					    return "assets/NotoMonoCJKkr.otf"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					from . import selectGlyphs
 | 
				
			||||||
 | 
					from . import getCachedFont
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Regular 같은 문자열 weight 를 포인트 값으로 변경
 | 
				
			||||||
 | 
					weightStrToNum = {
 | 
				
			||||||
 | 
					    "Regular": 16,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pasteGlyphs(target,sourcePath,deselectOriginalGlyphs,EnabledItems,baseSize=550,weightStr="Regular"):
 | 
				
			||||||
 | 
					    # 캐시된 소스를 읽어드림
 | 
				
			||||||
 | 
					    source = getCachedFont(
 | 
				
			||||||
 | 
					        sourcePath = sourcePath,
 | 
				
			||||||
 | 
					        baseSize = baseSize,
 | 
				
			||||||
 | 
					        weight = weightStrToNum.get(weightStr),
 | 
				
			||||||
 | 
					        EnabledItems = EnabledItems
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(source)
 | 
				
			||||||
 | 
					    selectGlyphs.Clear(target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 타겟으로 글리프 복사
 | 
				
			||||||
 | 
					    selectGlyphs.SelectByEnabledList(source,EnabledItems)
 | 
				
			||||||
 | 
					    deselectOriginalGlyphs(source)
 | 
				
			||||||
 | 
					    source.copy()
 | 
				
			||||||
 | 
					    selectGlyphs.SelectByEnabledList(source,target)
 | 
				
			||||||
 | 
					    deselectOriginalGlyphs(target)
 | 
				
			||||||
 | 
					    target.paste()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 캐시 닫기
 | 
				
			||||||
 | 
					    source.close()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					selFlag = ("more","ranges","unicode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 일본어 글립 선택
 | 
				
			||||||
 | 
					def JapaneseGlyphs(font):
 | 
				
			||||||
 | 
					    # 사각문자 (Square)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x32FF,0x2271)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x337B,0x337F) # Missing one char
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x1F200,0x1F200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 히라가나/가타카나
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3041,0x30FF)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x31F0,0x31FF) # SMALL Katakana
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xFF66,0xFF9F) # HalfWidth Katakana
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 일어 기호
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xFF5B,0xFF65)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CJK 한자 글립 선택
 | 
				
			||||||
 | 
					def CJKUnifiedIdeographs(font):
 | 
				
			||||||
 | 
					    # CJK Stroke
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x31C0,0x31E3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Bopomofo
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3105,0x312F)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x31A0,0x31BB)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # CJK Unified Ideograph
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x4E00,0x9FFF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def CJKUnifiedIdeographsExtension(font):
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3400,0x4DBF) # Extension A
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x20000,0x2A6DF) # Extension B
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x2A700,0x2B73F) # Extension C
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x2B740,0x2B81F) # Extension D
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x2B820,0x2CEAF) # Extension E
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x2CEB0,0x2EBEF) # Extension F
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def CJKCompatibilityIdeographs(font):
 | 
				
			||||||
 | 
					    # CJK Compatibility Ideograph
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xF900,0xFAFF)
 | 
				
			||||||
 | 
					    # CJK Compatibility Ideographs Supplement
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x2F800,0x2FA1F)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def Symbols(font):
 | 
				
			||||||
 | 
					    # 원형, 단위기호
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3220,0x3250)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3220,0x3250)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3280,0x32B0) # Missing ...
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x32C0,0x32FE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 단위 기호
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3358,0x337A)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x3380,0x33FF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Latin Ligature
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xFB00,0xFB04)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 특수기호/FULLWIDTH Latin
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xFE10,0xFF5A)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0xFFE0,0xFFEE)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x1F100,0x1F1AC)
 | 
				
			||||||
 | 
					    font.selection.select(selFlag,0x1F201,0x1F251)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def SelectByEnabledList(target,EnabledItems):
 | 
				
			||||||
 | 
					    if EnabledItems.get("JapaneseGlyphs"): JapaneseGlyphs(target)
 | 
				
			||||||
 | 
					    if EnabledItems.get("CJKUnifiedIdeographs"): CJKUnifiedIdeographs(target)
 | 
				
			||||||
 | 
					    if EnabledItems.get("CJKUnifiedIdeographsExtension"): CJKUnifiedIdeographsExtension(target)
 | 
				
			||||||
 | 
					    if EnabledItems.get("CJKCompatibilityIdeographs"): CJKCompatibilityIdeographs(target)
 | 
				
			||||||
 | 
					    if EnabledItems.get("Symbols"): Symbols(target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def Clear(font):
 | 
				
			||||||
 | 
					    font.selection.none()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .build import build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .NerdFonts import nerdFonts_Download_Test
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,91 @@
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import utility as Utility
 | 
				
			||||||
 | 
					import fontforge
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import NerdFonts as NerdFontsLoader
 | 
				
			||||||
 | 
					from . import NanumSquareNeo as NanumSquareNeoLoader
 | 
				
			||||||
 | 
					from . import NotoMono as NotoMonoLoader
 | 
				
			||||||
 | 
					from . import KawaiiMono as KawaiiMonoLoader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					deselectFlags = ("less","unicode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build(config=None):
 | 
				
			||||||
 | 
					    # 메인 폰트 불러오기 / 에셋 다운로드
 | 
				
			||||||
 | 
					    kawaii = fontforge.open(
 | 
				
			||||||
 | 
					        KawaiiMonoLoader.getFontPath())
 | 
				
			||||||
 | 
					    print("-------------- DOWNLOAD PATCH CONTENTS --------------")
 | 
				
			||||||
 | 
					    # 나눔 스퀘어 네오 다운로드
 | 
				
			||||||
 | 
					    nanumSquareNeo = None
 | 
				
			||||||
 | 
					    if config.get("CopyKoreanGlyphs"):
 | 
				
			||||||
 | 
					        nanumSquareNeo = NanumSquareNeoLoader.download()
 | 
				
			||||||
 | 
					    # 노토 모노 다운로드/불러오기
 | 
				
			||||||
 | 
					    notoMono = None
 | 
				
			||||||
 | 
					    if (config.get("CopyJapaneseGlyphs") or
 | 
				
			||||||
 | 
					        config.get("CopyCJKUnifiedIdeographs") or
 | 
				
			||||||
 | 
					        config.get("CopyCJKUnifiedIdeographsExtension") or
 | 
				
			||||||
 | 
					        config.get("CopyCJKCompatibilityIdeographs")):
 | 
				
			||||||
 | 
					        notoMono = NotoMonoLoader.download()
 | 
				
			||||||
 | 
					    # Nerd fonts 패치기 다운로드
 | 
				
			||||||
 | 
					    nerdFontsPatcherPath = None
 | 
				
			||||||
 | 
					    if config.get("NerdFonts"):
 | 
				
			||||||
 | 
					        nerdFontsPatcherPath = NerdFontsLoader.downloadPatcher()
 | 
				
			||||||
 | 
					    print("--------------------- Patching ---------------------")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 유지 목록 (덮어쓰기 금지) 만들기
 | 
				
			||||||
 | 
					    kawaii.selection.all()
 | 
				
			||||||
 | 
					    keepList = [i.unicode for i in kawaii.selection.byGlyphs]
 | 
				
			||||||
 | 
					    def deselectOriginalGlyphs(target):
 | 
				
			||||||
 | 
					        for unicode in keepList:
 | 
				
			||||||
 | 
					            if unicode == -1: continue
 | 
				
			||||||
 | 
					            target.selection.select(deselectFlags,unicode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 모든 글리프를 붇여넣을 수 있도록 인코딩을 utf full 로 변경
 | 
				
			||||||
 | 
					    kawaii.encoding = 'UnicodeFull'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 폰트 가로폭 설정
 | 
				
			||||||
 | 
					    baseSize = config.get("FontBaseWidth")
 | 
				
			||||||
 | 
					    if baseSize != 550:
 | 
				
			||||||
 | 
					        Utility.setWidthWithSavingPosition(
 | 
				
			||||||
 | 
					            font=kawaii,targetWidth=baseSize
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 한글 글리프 붇여넣기
 | 
				
			||||||
 | 
					    if nanumSquareNeo:
 | 
				
			||||||
 | 
					        # 글리프 붇여넣기
 | 
				
			||||||
 | 
					        NanumSquareNeoLoader.pasteGlyphs(
 | 
				
			||||||
 | 
					            target=kawaii,baseSize=baseSize,weightStr="Regular",
 | 
				
			||||||
 | 
					            sourcePath=nanumSquareNeo,
 | 
				
			||||||
 | 
					            deselectOriginalGlyphs = deselectOriginalGlyphs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 일어 글리프 혹은 한자 글리프 추가
 | 
				
			||||||
 | 
					    if notoMono:
 | 
				
			||||||
 | 
					        # 글리프 붇여넣기
 | 
				
			||||||
 | 
					        NotoMonoLoader.pasteGlyphs(
 | 
				
			||||||
 | 
					            EnabledItems = {
 | 
				
			||||||
 | 
					                "JapaneseGlyphs": config.get("CopyJapaneseGlyphs") or False,
 | 
				
			||||||
 | 
					                "CJKUnifiedIdeographs": config.get("CopyCJKUnifiedIdeographs") or False,
 | 
				
			||||||
 | 
					                "CJKUnifiedIdeographsExtension": config.get("CopyCJKUnifiedIdeographsExtension"),
 | 
				
			||||||
 | 
					                "CJKCompatibilityIdeographs": config.get("CopyCJKCompatibilityIdeographs"),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Symbols=config.get("CopySymbols") or False,
 | 
				
			||||||
 | 
					            target=kawaii,baseSize=550,
 | 
				
			||||||
 | 
					            sourcePath=notoMono,
 | 
				
			||||||
 | 
					            deselectOriginalGlyphs = deselectOriginalGlyphs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # NerdFonts 패치 적용
 | 
				
			||||||
 | 
					    # if config.get("NerdFonts"):
 | 
				
			||||||
 | 
					    #     nerdFontsPatcherPath = NerdFontsLoader.downloadPatcher()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 생성
 | 
				
			||||||
 | 
					    if not os.path.exists("out"): os.mkdir("out")
 | 
				
			||||||
 | 
					    kawaii.generate("out/"+"KawaiiMonoRegularPatched.ttf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # KawaiiMonoRegularPatched.ttf
 | 
				
			||||||
 | 
					    # KawaiiMonoRegularPatched.otf
 | 
				
			||||||
 | 
					    # KawaiiMonoRegularPatched.woff
 | 
				
			||||||
 | 
					    # KawaiiMonoRegularPatched.eot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 파일 닫기
 | 
				
			||||||
 | 
					    kawaii.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__": build()
 | 
				
			||||||
| 
						 | 
					@ -1,4 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
def pasteGlyphs(target,sourceList):
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .utility import *
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
 | 
					import psMat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setWidthWithSavingPosition(font,targetWidth):
 | 
					def setWidthWithSavingPosition(font,targetWidth):
 | 
				
			||||||
    for glyph in font.selection.byGlyphs:
 | 
					    for glyph in font.selection.byGlyphs:
 | 
				
			||||||
| 
						 | 
					@ -7,3 +8,7 @@ def setWidthWithSavingPosition(font,targetWidth):
 | 
				
			||||||
        glyph.left_side_bearing = int(glyph.left_side_bearing + math.floor(sideAdjust)) # 좌우 베어링을 조정함
 | 
					        glyph.left_side_bearing = int(glyph.left_side_bearing + math.floor(sideAdjust)) # 좌우 베어링을 조정함
 | 
				
			||||||
        glyph.right_side_bearing = int(glyph.right_side_bearing + math.ceil(sideAdjust))
 | 
					        glyph.right_side_bearing = int(glyph.right_side_bearing + math.ceil(sideAdjust))
 | 
				
			||||||
        glyph.width = targetWidth # 타겟 너비로 정확하게 설정
 | 
					        glyph.width = targetWidth # 타겟 너비로 정확하게 설정
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def scale(font,targetScale):
 | 
				
			||||||
 | 
					    font.transform(psMat.scale(targetScale))
 | 
				
			||||||
 | 
					    font.round()
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/wget
								
								
								
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								src/wget
								
								
								
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Subproject commit fdd3a0f8404ccab90f939f9952af139e6c55142a
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .wgetHandler import *
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,95 @@
 | 
				
			||||||
 | 
					Usage
 | 
				
			||||||
 | 
					=====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  python -m wget [options] <URL>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  options:
 | 
				
			||||||
 | 
					    -o --output FILE|DIR   output filename or directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					API Usage
 | 
				
			||||||
 | 
					=========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> import wget
 | 
				
			||||||
 | 
					  >>> url = 'http://www.futurecrew.com/skaven/song_files/mp3/razorback.mp3'
 | 
				
			||||||
 | 
					  >>> filename = wget.download(url)
 | 
				
			||||||
 | 
					  100% [................................................] 3841532 / 3841532>
 | 
				
			||||||
 | 
					  >> filename
 | 
				
			||||||
 | 
					  'razorback.mp3'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The skew that you see above is a documented side effect.
 | 
				
			||||||
 | 
					Alternative progress bar:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> wget.download(url, bar=bar_thermometer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChangeLog
 | 
				
			||||||
 | 
					=========
 | 
				
			||||||
 | 
					2.2 (2014-07-19)
 | 
				
			||||||
 | 
					 * it again can download without -o option
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.1 (2014-07-10)
 | 
				
			||||||
 | 
					 * it shows command line help
 | 
				
			||||||
 | 
					 * -o option allows to select output file/directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   * download(url, out, bar) contains out parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.0 (2013-04-26)
 | 
				
			||||||
 | 
					 * it shows percentage
 | 
				
			||||||
 | 
					 * it has usage examples
 | 
				
			||||||
 | 
					 * it changes if being used as a library
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   * download shows progress bar by default
 | 
				
			||||||
 | 
					   * bar_adaptive gets improved algorithm
 | 
				
			||||||
 | 
					   * download(url, bar) contains bar parameter
 | 
				
			||||||
 | 
					     * bar(current, total)
 | 
				
			||||||
 | 
					   * progress_callback is named callback_progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.0 (2012-11-13)
 | 
				
			||||||
 | 
					 * it runs with Python 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.9 (2012-11-13)
 | 
				
			||||||
 | 
					 * it renames file if it already exists
 | 
				
			||||||
 | 
					 * it can be used as a library
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   * download(url) returns filename
 | 
				
			||||||
 | 
					   * bar_adaptive() draws progress bar
 | 
				
			||||||
 | 
					   * bar_thermometer() simplified bar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.8 (2011-05-03)
 | 
				
			||||||
 | 
					 * it detects filename from HTTP headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.7 (2011-03-01)
 | 
				
			||||||
 | 
					 * compatibility fix for Python 2.5
 | 
				
			||||||
 | 
					 * limit width of progress bar to 100 chars
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.6 (2010-04-24)
 | 
				
			||||||
 | 
					 * it detects console width on POSIX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.5 (2010-04-23)
 | 
				
			||||||
 | 
					 * it detects console width on Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.4 (2010-04-15)
 | 
				
			||||||
 | 
					 * it shows cute progress bar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.3 (2010-04-05)
 | 
				
			||||||
 | 
					 * it creates temp file in current dir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.2 (2010-02-16)
 | 
				
			||||||
 | 
					 * it tries to detect filename from URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.1 (2010-02-04)
 | 
				
			||||||
 | 
					 * it can download file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Release Checklist
 | 
				
			||||||
 | 
					=================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| [ ] update version in wget.py
 | 
				
			||||||
 | 
					| [x] update description in setup.py
 | 
				
			||||||
 | 
					| [ ] python setup.py check -mrs
 | 
				
			||||||
 | 
					| [ ] python setup.py sdist upload
 | 
				
			||||||
 | 
					| [ ] tag hg version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- 
 | 
				
			||||||
 | 
					anatoly techtonik <techtonik@gmail.com>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					from distutils.core import setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_version(relpath):
 | 
				
			||||||
 | 
					    """read version info from file without importing it"""
 | 
				
			||||||
 | 
					    from os.path import dirname, join
 | 
				
			||||||
 | 
					    for line in open(join(dirname(__file__), relpath)):
 | 
				
			||||||
 | 
					        if '__version__' in line:
 | 
				
			||||||
 | 
					            if '"' in line:
 | 
				
			||||||
 | 
					                # __version__ = "0.9"
 | 
				
			||||||
 | 
					                return line.split('"')[1]
 | 
				
			||||||
 | 
					            elif "'" in line:
 | 
				
			||||||
 | 
					                return line.split("'")[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup(
 | 
				
			||||||
 | 
					    name='wget',
 | 
				
			||||||
 | 
					    version=get_version('wget.py'),
 | 
				
			||||||
 | 
					    author='anatoly techtonik <techtonik@gmail.com>',
 | 
				
			||||||
 | 
					    url='http://bitbucket.org/techtonik/python-wget/',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    description="pure python download utility",
 | 
				
			||||||
 | 
					    license="Public Domain",
 | 
				
			||||||
 | 
					    classifiers=[
 | 
				
			||||||
 | 
					        'Environment :: Console',
 | 
				
			||||||
 | 
					        'License :: Public Domain',
 | 
				
			||||||
 | 
					        'Operating System :: OS Independent',
 | 
				
			||||||
 | 
					        'Programming Language :: Python :: 2',
 | 
				
			||||||
 | 
					        'Programming Language :: Python :: 3',
 | 
				
			||||||
 | 
					        'Topic :: Software Development :: Libraries :: Python Modules',
 | 
				
			||||||
 | 
					        'Topic :: System :: Networking',
 | 
				
			||||||
 | 
					        'Topic :: Utilities',
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    py_modules=['wget'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    long_description=open('README.txt').read(),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,402 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Download utility as an easy way to get file from the net
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  python -m wget <URL>
 | 
				
			||||||
 | 
					  python wget.py <URL>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Downloads: http://pypi.python.org/pypi/wget/
 | 
				
			||||||
 | 
					Development: http://bitbucket.org/techtonik/python-wget/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wget.py is not option compatible with Unix wget utility,
 | 
				
			||||||
 | 
					to make command line interface intuitive for new people.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Public domain by anatoly techtonik <techtonik@gmail.com>
 | 
				
			||||||
 | 
					Also available under the terms of MIT license
 | 
				
			||||||
 | 
					Copyright (c) 2010-2014 anatoly techtonik
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys, shutil, os
 | 
				
			||||||
 | 
					import tempfile
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PY3K = sys.version_info >= (3, 0)
 | 
				
			||||||
 | 
					if PY3K:
 | 
				
			||||||
 | 
					  import urllib.request as urllib
 | 
				
			||||||
 | 
					  import urllib.parse as urlparse
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					  import urllib
 | 
				
			||||||
 | 
					  import urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = "2.3-beta1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def filename_from_url(url):
 | 
				
			||||||
 | 
					    """:return: detected filename or None"""
 | 
				
			||||||
 | 
					    fname = os.path.basename(urlparse.urlparse(url).path)
 | 
				
			||||||
 | 
					    if len(fname.strip(" \n\t.")) == 0:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    return fname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def filename_from_headers(headers):
 | 
				
			||||||
 | 
					    """Detect filename from Content-Disposition headers if present.
 | 
				
			||||||
 | 
					    http://greenbytes.de/tech/tc2231/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param: headers as dict, list or string
 | 
				
			||||||
 | 
					    :return: filename from content-disposition header or None
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if type(headers) == str:
 | 
				
			||||||
 | 
					        headers = headers.splitlines()
 | 
				
			||||||
 | 
					    if type(headers) == list:
 | 
				
			||||||
 | 
					        headers = dict([x.split(':', 1) for x in headers])
 | 
				
			||||||
 | 
					    cdisp = headers.get("Content-Disposition")
 | 
				
			||||||
 | 
					    if not cdisp:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    cdtype = cdisp.split(';')
 | 
				
			||||||
 | 
					    if len(cdtype) == 1:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    if cdtype[0].strip().lower() not in ('inline', 'attachment'):
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    # several filename params is illegal, but just in case
 | 
				
			||||||
 | 
					    fnames = [x for x in cdtype[1:] if x.strip().startswith('filename=')]
 | 
				
			||||||
 | 
					    if len(fnames) > 1:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    name = fnames[0].split('=')[1].strip(' \t"')
 | 
				
			||||||
 | 
					    name = os.path.basename(name)
 | 
				
			||||||
 | 
					    if not name:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    return name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def filename_fix_existing(filename):
 | 
				
			||||||
 | 
					    """Expands name portion of filename with numeric ' (x)' suffix to
 | 
				
			||||||
 | 
					    return filename that doesn't exist already.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    dirname = '.' 
 | 
				
			||||||
 | 
					    name, ext = filename.rsplit('.', 1)
 | 
				
			||||||
 | 
					    names = [x for x in os.listdir(dirname) if x.startswith(name)]
 | 
				
			||||||
 | 
					    names = [x.rsplit('.', 1)[0] for x in names]
 | 
				
			||||||
 | 
					    suffixes = [x.replace(name, '') for x in names]
 | 
				
			||||||
 | 
					    # filter suffixes that match ' (x)' pattern
 | 
				
			||||||
 | 
					    suffixes = [x[2:-1] for x in suffixes
 | 
				
			||||||
 | 
					                   if x.startswith(' (') and x.endswith(')')]
 | 
				
			||||||
 | 
					    indexes  = [int(x) for x in suffixes
 | 
				
			||||||
 | 
					                   if set(x) <= set('0123456789')]
 | 
				
			||||||
 | 
					    idx = 1
 | 
				
			||||||
 | 
					    if indexes:
 | 
				
			||||||
 | 
					        idx += sorted(indexes)[-1]
 | 
				
			||||||
 | 
					    return '%s (%d).%s' % (name, idx, ext)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# --- terminal/console output helpers ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_console_width():
 | 
				
			||||||
 | 
					    """Return width of available window area. Autodetection works for
 | 
				
			||||||
 | 
					       Windows and POSIX platforms. Returns 80 for others
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       Code from http://bitbucket.org/techtonik/python-pager
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if os.name == 'nt':
 | 
				
			||||||
 | 
					        STD_INPUT_HANDLE  = -10
 | 
				
			||||||
 | 
					        STD_OUTPUT_HANDLE = -11
 | 
				
			||||||
 | 
					        STD_ERROR_HANDLE  = -12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get console handle
 | 
				
			||||||
 | 
					        from ctypes import windll, Structure, byref
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            from ctypes.wintypes import SHORT, WORD, DWORD
 | 
				
			||||||
 | 
					        except ImportError:
 | 
				
			||||||
 | 
					            # workaround for missing types in Python 2.5
 | 
				
			||||||
 | 
					            from ctypes import (
 | 
				
			||||||
 | 
					                c_short as SHORT, c_ushort as WORD, c_ulong as DWORD)
 | 
				
			||||||
 | 
					        console_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # CONSOLE_SCREEN_BUFFER_INFO Structure
 | 
				
			||||||
 | 
					        class COORD(Structure):
 | 
				
			||||||
 | 
					            _fields_ = [("X", SHORT), ("Y", SHORT)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class SMALL_RECT(Structure):
 | 
				
			||||||
 | 
					            _fields_ = [("Left", SHORT), ("Top", SHORT),
 | 
				
			||||||
 | 
					                        ("Right", SHORT), ("Bottom", SHORT)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class CONSOLE_SCREEN_BUFFER_INFO(Structure):
 | 
				
			||||||
 | 
					            _fields_ = [("dwSize", COORD),
 | 
				
			||||||
 | 
					                        ("dwCursorPosition", COORD),
 | 
				
			||||||
 | 
					                        ("wAttributes", WORD),
 | 
				
			||||||
 | 
					                        ("srWindow", SMALL_RECT),
 | 
				
			||||||
 | 
					                        ("dwMaximumWindowSize", DWORD)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sbi = CONSOLE_SCREEN_BUFFER_INFO()
 | 
				
			||||||
 | 
					        ret = windll.kernel32.GetConsoleScreenBufferInfo(console_handle, byref(sbi))
 | 
				
			||||||
 | 
					        if ret == 0:
 | 
				
			||||||
 | 
					            return 0
 | 
				
			||||||
 | 
					        return sbi.srWindow.Right+1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif os.name == 'posix':
 | 
				
			||||||
 | 
					        from fcntl import ioctl
 | 
				
			||||||
 | 
					        from termios import TIOCGWINSZ
 | 
				
			||||||
 | 
					        from array import array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        winsize = array("H", [0] * 4)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            ioctl(sys.stdout.fileno(), TIOCGWINSZ, winsize)
 | 
				
			||||||
 | 
					        except IOError:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        return (winsize[1], winsize[0])[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 80
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def bar_thermometer(current, total, width=80):
 | 
				
			||||||
 | 
					    """Return thermometer style progress bar string. `total` argument
 | 
				
			||||||
 | 
					    can not be zero. The minimum size of bar returned is 3. Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [..........            ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Control and trailing symbols (\r and spaces) are not included.
 | 
				
			||||||
 | 
					    See `bar_adaptive` for more information.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    # number of dots on thermometer scale
 | 
				
			||||||
 | 
					    avail_dots = width-2
 | 
				
			||||||
 | 
					    shaded_dots = int(math.floor(float(current) / total * avail_dots))
 | 
				
			||||||
 | 
					    return '[' + '.'*shaded_dots + ' '*(avail_dots-shaded_dots) + ']'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def bar_adaptive(current, total, width=80):
 | 
				
			||||||
 | 
					    """Return progress bar string for given values in one of three
 | 
				
			||||||
 | 
					    styles depending on available width:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [..  ] downloaded / total
 | 
				
			||||||
 | 
					        downloaded / total
 | 
				
			||||||
 | 
					        [.. ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if total value is unknown or <= 0, show bytes counter using two
 | 
				
			||||||
 | 
					    adaptive styles:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        %s / unknown
 | 
				
			||||||
 | 
					        %s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if there is not enough space on the screen, do not display anything
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    returned string doesn't include control characters like \r used to
 | 
				
			||||||
 | 
					    place cursor at the beginning of the line to erase previous content.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this function leaves one free character at the end of string to
 | 
				
			||||||
 | 
					    avoid automatic linefeed on Windows.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # process special case when total size is unknown and return immediately
 | 
				
			||||||
 | 
					    if not total or total < 0:
 | 
				
			||||||
 | 
					        msg = "%s / unknown" % current
 | 
				
			||||||
 | 
					        if len(msg) < width:    # leaves one character to avoid linefeed
 | 
				
			||||||
 | 
					            return msg
 | 
				
			||||||
 | 
					        if len("%s" % current) < width:
 | 
				
			||||||
 | 
					            return "%s" % current
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # --- adaptive layout algorithm ---
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # [x] describe the format of the progress bar
 | 
				
			||||||
 | 
					    # [x] describe min width for each data field
 | 
				
			||||||
 | 
					    # [x] set priorities for each element
 | 
				
			||||||
 | 
					    # [x] select elements to be shown
 | 
				
			||||||
 | 
					    #   [x] choose top priority element min_width < avail_width
 | 
				
			||||||
 | 
					    #   [x] lessen avail_width by value if min_width
 | 
				
			||||||
 | 
					    #   [x] exclude element from priority list and repeat
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #  10% [.. ]  10/100
 | 
				
			||||||
 | 
					    # pppp bbbbb sssssss
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    min_width = {
 | 
				
			||||||
 | 
					      'percent': 4,  # 100%
 | 
				
			||||||
 | 
					      'bar': 3,      # [.]
 | 
				
			||||||
 | 
					      'size': len("%s" % total)*2 + 3, # 'xxxx / yyyy'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    priority = ['percent', 'bar', 'size']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # select elements to show
 | 
				
			||||||
 | 
					    selected = []
 | 
				
			||||||
 | 
					    avail = width
 | 
				
			||||||
 | 
					    for field in priority:
 | 
				
			||||||
 | 
					      if min_width[field] < avail:
 | 
				
			||||||
 | 
					        selected.append(field)
 | 
				
			||||||
 | 
					        avail -= min_width[field]+1   # +1 is for separator or for reserved space at
 | 
				
			||||||
 | 
					                                      # the end of line to avoid linefeed on Windows
 | 
				
			||||||
 | 
					    # render
 | 
				
			||||||
 | 
					    output = ''
 | 
				
			||||||
 | 
					    for field in selected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if field == 'percent':
 | 
				
			||||||
 | 
					        # fixed size width for percentage
 | 
				
			||||||
 | 
					        output += ('%s%%' % (100 * current // total)).rjust(min_width['percent'])
 | 
				
			||||||
 | 
					      elif field == 'bar':  # [. ]
 | 
				
			||||||
 | 
					        # bar takes its min width + all available space
 | 
				
			||||||
 | 
					        output += bar_thermometer(current, total, min_width['bar']+avail)
 | 
				
			||||||
 | 
					      elif field == 'size':
 | 
				
			||||||
 | 
					        # size field has a constant width (min == max)
 | 
				
			||||||
 | 
					        output += ("%s / %s" % (current, total)).rjust(min_width['size'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      selected = selected[1:]
 | 
				
			||||||
 | 
					      if selected:
 | 
				
			||||||
 | 
					        output += ' '  # add field separator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# --/ console helpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__current_size = 0  # global state variable, which exists solely as a
 | 
				
			||||||
 | 
					                    # workaround against Python 3.3.0 regression
 | 
				
			||||||
 | 
					                    # http://bugs.python.org/issue16409
 | 
				
			||||||
 | 
					                    # fixed in Python 3.3.1
 | 
				
			||||||
 | 
					def callback_progress(blocks, block_size, total_size, bar_function):
 | 
				
			||||||
 | 
					    """callback function for urlretrieve that is called when connection is
 | 
				
			||||||
 | 
					    created and when once for each block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    draws adaptive progress bar in terminal/console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use sys.stdout.write() instead of "print,", because it allows one more
 | 
				
			||||||
 | 
					    symbol at the line end without linefeed on Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param blocks: number of blocks transferred so far
 | 
				
			||||||
 | 
					    :param block_size: in bytes
 | 
				
			||||||
 | 
					    :param total_size: in bytes, can be -1 if server doesn't return it
 | 
				
			||||||
 | 
					    :param bar_function: another callback function to visualize progress
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    global __current_size
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    width = min(100, get_console_width())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.version_info[:3] == (3, 3, 0):  # regression workaround
 | 
				
			||||||
 | 
					        if blocks == 0:  # first call
 | 
				
			||||||
 | 
					            __current_size = 0
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            __current_size += block_size
 | 
				
			||||||
 | 
					        current_size = __current_size
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        current_size = min(blocks*block_size, total_size)
 | 
				
			||||||
 | 
					    progress = bar_function(current_size, total_size, width)
 | 
				
			||||||
 | 
					    if progress:
 | 
				
			||||||
 | 
					        sys.stdout.write("\r" + progress)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ThrowOnErrorOpener(urllib.FancyURLopener):
 | 
				
			||||||
 | 
					    def http_error_default(self, url, fp, errcode, errmsg, headers):
 | 
				
			||||||
 | 
					        raise Exception("%s: %s" % (errcode, errmsg))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def download(url, out=None, bar=bar_adaptive):
 | 
				
			||||||
 | 
					    """High level function, which downloads URL into tmp file in current
 | 
				
			||||||
 | 
					    directory and then renames it to filename autodetected from either URL
 | 
				
			||||||
 | 
					    or HTTP headers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param bar: function to track download progress (visualize etc.)
 | 
				
			||||||
 | 
					    :param out: output filename or directory
 | 
				
			||||||
 | 
					    :return:    filename where URL is downloaded to
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    names = dict()
 | 
				
			||||||
 | 
					    names["out"] = out or ''
 | 
				
			||||||
 | 
					    names["url"] = filename_from_url(url)
 | 
				
			||||||
 | 
					    # get filename for temp file in current directory
 | 
				
			||||||
 | 
					    prefix = (names["url"] or names["out"] or ".") + "."
 | 
				
			||||||
 | 
					    (fd, tmpfile) = tempfile.mkstemp(".tmp", prefix=prefix, dir=".")
 | 
				
			||||||
 | 
					    os.close(fd)
 | 
				
			||||||
 | 
					    os.unlink(tmpfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set progress monitoring callback
 | 
				
			||||||
 | 
					    def callback_charged(blocks, block_size, total_size):
 | 
				
			||||||
 | 
					        # 'closure' to set bar drawing function in callback
 | 
				
			||||||
 | 
					        callback_progress(blocks, block_size, total_size, bar_function=bar)
 | 
				
			||||||
 | 
					    if bar:
 | 
				
			||||||
 | 
					        callback = callback_charged
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        callback = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (tmpfile, headers) = ThrowOnErrorOpener().retrieve(url, tmpfile, callback)
 | 
				
			||||||
 | 
					    names["header"] = filename_from_headers(headers)
 | 
				
			||||||
 | 
					    if os.path.isdir(names["out"]):
 | 
				
			||||||
 | 
					        filename = names["header"] or names["url"]
 | 
				
			||||||
 | 
					        filename = names["out"] + "/" + filename
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        filename = names["out"] or names["header"] or names["url"]
 | 
				
			||||||
 | 
					    # add numeric ' (x)' suffix if filename already exists
 | 
				
			||||||
 | 
					    if os.path.exists(filename):
 | 
				
			||||||
 | 
					        filename = filename_fix_existing(filename)
 | 
				
			||||||
 | 
					    shutil.move(tmpfile, filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #print headers
 | 
				
			||||||
 | 
					    return filename
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					usage = """\
 | 
				
			||||||
 | 
					usage: wget.py [options] URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					options:
 | 
				
			||||||
 | 
					  -o --output FILE|DIR   output filename or directory
 | 
				
			||||||
 | 
					  -h --help
 | 
				
			||||||
 | 
					  --version
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    if len(sys.argv) < 2 or "-h" in sys.argv or "--help" in sys.argv:
 | 
				
			||||||
 | 
					        sys.exit(usage)
 | 
				
			||||||
 | 
					    if "--version" in sys.argv:
 | 
				
			||||||
 | 
					        sys.exit("wget.py " + __version__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from optparse import OptionParser
 | 
				
			||||||
 | 
					    parser = OptionParser()
 | 
				
			||||||
 | 
					    parser.add_option("-o", "--output", dest="output")
 | 
				
			||||||
 | 
					    (options, args) = parser.parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    url = sys.argv[1]
 | 
				
			||||||
 | 
					    filename = download(args[0], out=options.output)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("")
 | 
				
			||||||
 | 
					    print("Saved under %s" % filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					r"""
 | 
				
			||||||
 | 
					features that require more tuits for urlretrieve API
 | 
				
			||||||
 | 
					http://www.python.org/doc/2.6/library/urllib.html#urllib.urlretrieve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[x] autodetect filename from URL
 | 
				
			||||||
 | 
					[x] autodetect filename from headers - Content-Disposition
 | 
				
			||||||
 | 
					    http://greenbytes.de/tech/tc2231/
 | 
				
			||||||
 | 
					[ ] make HEAD request to detect temp filename from Content-Disposition
 | 
				
			||||||
 | 
					[ ] process HTTP status codes (i.e. 404 error)
 | 
				
			||||||
 | 
					    http://ftp.de.debian.org/debian/pool/iso-codes_3.24.2.orig.tar.bz2
 | 
				
			||||||
 | 
					[ ] catch KeyboardInterrupt
 | 
				
			||||||
 | 
					[ ] optionally preserve incomplete file
 | 
				
			||||||
 | 
					[x] create temp file in current directory
 | 
				
			||||||
 | 
					[ ] resume download (broken connection)
 | 
				
			||||||
 | 
					[ ] resume download (incomplete file)
 | 
				
			||||||
 | 
					[x] show progress indicator
 | 
				
			||||||
 | 
					    http://mail.python.org/pipermail/tutor/2005-May/038797.html
 | 
				
			||||||
 | 
					[x] do not overwrite downloaded file
 | 
				
			||||||
 | 
					 [x] rename file automatically if exists
 | 
				
			||||||
 | 
					[x] optionally specify path for downloaded file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ ] options plan
 | 
				
			||||||
 | 
					 [x] -h, --help, --version (CHAOS speccy)
 | 
				
			||||||
 | 
					[ ] clpbar progress bar style
 | 
				
			||||||
 | 
					_ 30.0Mb at  3.0 Mbps  eta:   0:00:20   30% [=====         ]
 | 
				
			||||||
 | 
					[ ] test "bar \r" print with \r at the end of line on Windows
 | 
				
			||||||
 | 
					[ ] process Python 2.x urllib.ContentTooShortError exception gracefully
 | 
				
			||||||
 | 
					    (ideally retry and continue download)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (tmpfile, headers) = urllib.urlretrieve(url, tmpfile, callback_progress)
 | 
				
			||||||
 | 
					  File "C:\Python27\lib\urllib.py", line 93, in urlretrieve
 | 
				
			||||||
 | 
					    return _urlopener.retrieve(url, filename, reporthook, data)
 | 
				
			||||||
 | 
					  File "C:\Python27\lib\urllib.py", line 283, in retrieve
 | 
				
			||||||
 | 
					    "of %i bytes" % (read, size), result)
 | 
				
			||||||
 | 
					urllib.ContentTooShortError: retrieval incomplete: got only 15239952 out of 24807571 bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ ] find out if urlretrieve may return unicode headers
 | 
				
			||||||
 | 
					[ ] test suite for unsafe filenames from url and from headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ ] security checks
 | 
				
			||||||
 | 
					  [ ] filename_from_url
 | 
				
			||||||
 | 
					  [ ] filename_from_headers
 | 
				
			||||||
 | 
					  [ ] MITM redirect from https URL
 | 
				
			||||||
 | 
					  [ ] https certificate check
 | 
				
			||||||
 | 
					  [ ] size+hash check helpers
 | 
				
			||||||
 | 
					    [ ] fail if size is known and mismatch
 | 
				
			||||||
 | 
					    [ ] fail if hash mismatch
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
from .wget import wget
 | 
					 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .wget import wget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# bar 를 커스텀.... 하는 어떤 글에서 가져온거
 | 
					# bar 를 커스텀.... 하는 어떤 글에서 가져온거
 | 
				
			||||||
def bar_custom(current, total, width=80):
 | 
					def bar_custom(current, total, width=80):
 | 
				
			||||||
    width=30
 | 
					    width=30
 | 
				
			||||||
		Loading…
	
		Reference in New Issue