본문 바로가기
아래아한글 자동화/python+hwp 중급

[2/4] 정관문서 서식잡기(장이름, 조제목만 굵게)

by 일코 2020. 12. 11.

지난 포스팅은...

 

[1/4] 정관문서 서식잡기(조번호 앞 공백 추가)

1번 질문:=========================== 제[공백][공백]1조 제[공백][공백]2조 . . 제[공백]13조 제[공백]14조 등과 같이 앞에 3자리를 기준으로 빈 공백을 놓아 두고 싶습니다.... 그러면 ”조“자의 위치가 나

www.martinii.fun



부탁 드렸던 방법은 제가 회사의 정관을 많이 손 보아야 하는 직업이라.

수년전 한글내에서 매크로로 겨우 조항 제목 문자 굵게 만들고,

2번 질문====================== 
제 1조(상호)
제 2조(목적)을 다음과 같이 굵게(BOLD) 하고 싶습니다.
제 1조(상호) 굵게
제 2조(목적) 굵게
이와 같이 굵게 칠하고 싶습니다. -
(후략)

한 구독자 분으로부터 문의메일을 받고 자문을 드렸던 내용을 포스팅으로 올리는 중입니다.

일명 "정관서식 자동교정(?)" 시리즈입니다. 총 5개의 포스팅으로 연재 예정입니다.

0. [준비] 조항번호 재정렬
1. 조항번호 앞 공백 추가(제1조 -> 제__1조, 제10조 -> 제_10조)
2. 장제목 및 조항제목이 굵지 않다면 굵게
3. 장제목은 센터정렬, 장제목 위아래 빈줄, 조제목 위에 빈줄
4. 종합

 

정관의 초기 서식은 아래와 같습니다.

정관의 초기 서식

지난 포스팅에서는 조항 번호 앞에 임의의 공백을 넣어 조항 괄호끼리 같은 열에서 시작하게 만들었습니다.

예 : 제1조() -> 제  1조(), 제10조() -> 제 10조()

2020/12/11 - [파이썬-아래아한글 자동화 응용] - [1/4] 정관문서 서식잡기(조번호 앞 공백 추가)

이번 시리즈를 마칠 때, 최종적으로 제가 원하는 서식은 아래와 같습니다.

정관의 최종 서식


이번 포스팅에서는 장제목, 조항제목 부분에 "굵게(BOLD)" 서식을 적용해 보겠습니다. 질문 주신 분께서는 조항명만 말씀하셨지만요.

이 자동화작업은 사실 간단해 보이지만, 굵직한 세 개의 작업이 필요합니다.

1. 현재 선택구간이 "굵게" 적용상태인지 체크하는 함수 필요

현재 "진하게"가 적용된 상태인지, 그렇지 않은지 확인해야 함 : 왜냐면 아래아한글의 "진하게(Ctrl-B)" 메서드인 hwp.Run("CharShapeBold")는 정확히는 "진하게"가 아니고, "진하게 토글"입니다. 무슨 말이냐면, 이미 진하게 적용되어 있는 부분을 블록선택하고 저 메서드를 적용하면 진하지 않게 되어 버립니다. 그래서 현재 진한 상태인지를 체크하는 함수를 추가해야만 오류 없이 굴러갈 수 있습니다. (다행히 그런 함수는 간단히 만들 수 있습니다.)

2. 원하는 문자열을 찾고, 그 위치로 찾아가서, 원하는 구간만큼만 선택하는 함수 필요

원하는 구간에 "굵게"를 적용하려면 해당 구간을 선택해야 하는데? 한두 번 이런 작업을 해 보셨거나, 노하우가 있는 분들은 어렵지 않은 부분인데, 자동화가 생소하신 분들은 이 부분도 막힐 수 있습니다. 저 같은 경우는 이렇게 접근해봅니다.

"굵게" 적용하기 위해 선택해야 하는 구간

"제?조"로 시작하는 문단 머리로 찾아가서 가장 먼저 나오는 ") "를 찾고 문단 머리부터 괄호까지 선택한다.
(찾기Ctrl-F 한 번 실행하면 그 상태가 됨)

혹은

"제?조"로 시작하는 문단 머리로 찾아간 후 ") "를 찾아 캐럿을 옮기고, 그 상태에서
Shift-Home [코드로는 hwp.Run("MoveSelLineBegin")]을 누른다.

두 번째 방법이 근소하지만 더 간단할 것 같아요.

장제목은 어떨까요? 더 간단하겠죠? 

"제?장"으로 시작하는 구간으로 찾아가서 Shift-End [코드로는 hwp.Run("MoveSelLineEnd")]

만 추가하시면 장제목 전체가 선택될 겁니다.

[참고] 본 문서파일의 깨짐이 있는 건지, 메모리 때문인지, FindText()를 실행하는 중에 특정 문단에서 계속 탐색이 종료되었습니다. 그 부분을 해결하기 위해 메인함수(hwp_find_and_get_bold)의 if문이 조금 길어졌습니다. 여러분 PC에서나 일반적인 다른 문서에서는 이런 일이 발생하지 않을 것 같아, 해당코드를 뺄까 하다가 놔뒀습니다. 아래 소스코드 67~70라인 구간입니다.

내용설명이 상당히 길었습니다. 실제 구현화면은 아래와 같습니다. (영상은 디버깅으로 천천히 실행한 버전)

파이썬으로 특정 구간 찾아서 "굵게" 적용하는 영상

파이썬으로 특정 구간 찾아서 "굵게" 적용하는 영상(움짤)


아래는 작업전 HWP파일, 작업후 HWP파일과 소스코드입니다. ("굵게" 적용한 것 말고는 아무 차이 없습니다.)

작업 전 파일

1번완료.hwp
0.14MB

작업 후 파일

2번완료.hwp
0.13MB

"""
2번 질문======================
제 1조(상호)
제 2조(목적)을 다음과 같이 굵게(BOLD) 하고 싶습니다.
제 1조(상호) 굵게
제 2조(목적) 굵게
이와 같이 굵게 칠하고 싶습니다. -
"""


from time import sleep  # 너무 빨리 실행돼서 오류가 났나 싶어 써본 기능(일시정지)
from tkinter import Tk  # 다이얼로그창 모듈 임포트
from tkinter.filedialog import askopenfilename  # HWP파일선택을 위한 다이얼로그창
import re  # 정규표현식
import win32com.client as win32  # 한/글 오브젝트 조작을 위한 win32 임포트


def hwp_get_max_number(hwp):  # 조항 갯수 추출코드
	"""중간에 탐색을 멈추는 현상이 지속적으로 발생하여, 
    마지막 조항에 도착하지 않았으면 계속 탐색을 처음부터 재시도하도록 코드 변경."""
    text = hwp.GetTextFile("TEXT", "").split("\r\n")  # 모든 텍스트를 전부 긁어서
    max_number = max([int(i.replace(" ", "")[1:].split("조")[0].strip()) for i in text if i.startswith("제")])
    # "제?조"로 시작하는 문자열의 ?만 추출해서 정수로 바꾸고 그 리스트 중 최대값을 max_number로 지정함
    return max_number


def hwp_get_bold(hwp, type):  # "굵게" 적용 전에 현재 굵은 상태인지 체크하는 코드 추가(위6줄)
    Act = hwp.CreateAction("CharShape")  # 액션테이블에서 "글자 모양" 검색, 액션아이디에서 "CharShape" 찾음
    Set = Act.CreateSet()  # 세트 생성
    Act.GetDefault(Set)  # 세트 초기화(Set의 파라미터에 현재 문서의 값을 적용)
    if Set.Item("Bold") == 1:  # 파라미터셋테이블에서 "CharShape" 검색, 아이템아이디에서 "Bold" 찾음
        return  # 굵은 상태면 함수종료. 
    else:  # 0이나 None을 리턴하는 경우가 있는데, 0은 모두 진하지 않은 경우, None은 섞인 경우. 둘 다 아래코드 실행됨
        if type == "장":  # "제?장"의 경우
            hwp.HAction.Run("MoveSelLineEnd")  # 라인 끝까지 선택하고
            hwp.HAction.Run("CharShapeBold")  # 굵게Ctrl-B 커맨드 입력
            hwp.HAction.Run("MoveLineBegin")  # 다시 라인 시작점으로 이동
        elif type == "조":  # "제?조"의 경우
            # 아래는 라인 시작점에서 가장 먼저 나오는 ")"를 찾아서 그 지점부터 시작점까지 선택하고 진하게 적용.
            hwp.HAction.GetDefault("RepeatFind", hwp.HParameterSet.HFindReplace.HSet)
            hwp.HParameterSet.HFindReplace.FindString = ")"
            hwp.HParameterSet.HFindReplace.Direction = hwp.FindDir("Forward")
            hwp.HParameterSet.HFindReplace.IgnoreMessage = 1
            hwp.HParameterSet.HFindReplace.FindType = 1
            hwp.HAction.Execute("RepeatFind", hwp.HParameterSet.HFindReplace.HSet)
            hwp.HAction.Run("MoveRight")
            hwp.HAction.Run("MoveLeft")
            hwp.HAction.Run("MoveSelLineBegin")
            hwp.HAction.Run("CharShapeBold")
            hwp.HAction.Run("MoveLineBegin")
        else:  # 장도 조도 아닌 게 선택되어 있으면?
            raise TypeError  # 선택에서든 탐색에서든 명백한 오류이므로 에러발생


def hwp_init(filename):  # 아래아한글 열기
    hwp = win32.gencache.EnsureDispatch("HWPFrame.HwpObject")  # 한/글 오브젝트 생성
    hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule")  # 보안모듈 실행
    hwp.Open(filename)  # 최초에 선택한 HWP파일 불러오기
    hwp.XHwpWindows.Item(0).Visible = True  # 숨김해제(열 때 기본값이 "숨김")
    hwp.HAction.Run("FrameFullScreen")  # 전체화면
    return hwp


def hwp_find_and_get_bold(hwp):  # 찾아가서 굵게 하는 메인함수.
    max_number = hwp_get_max_number(hwp)  # 우선 마지막 조항번호를 알아내고
    hwp.InitScan() # 탐색시작 커맨드
    장번호 = 1  # 장-인덱스
    조항번호 = 1  # 조-인덱스
    while True:  # break 만날 때까지 무한반복
        text = hwp.GetText()  # 다음라인 탐색
        if text[0] == 1 and 조항번호 != max_number:  # 검색이 갑자기 종료되는 버그(문서 손상? 메모리 문제? 초기화하면 정상작동함..)
            hwp.ReleaseScan()  # 마지막 조항까지 도착 안했는데 탐색이 종료되면, 탐색프로세스 종료하고
            hwp.InitScan()  # 새로운 탐색프로세스 생성
            sleep(0.1)  # 쉬어가자. 
            continue  # while문 재개
        elif 조항번호 > max_number:  # 마지막 조항까지 도달했으면
            break  # while문 종료
        else:  # 그 외 일반적인 경우에는 
            if re.match(rf"^제{조항번호}조\(?", text[1].replace(" ", "")):  # "제?조"를 찾았으면
                hwp.MovePos(201)  # 해당 라인 시작점으로 가서
                hwp_get_bold(hwp, "조") # 위의 굵게함수 적용
                조항번호 += 1  # 조-인덱스 + 1
                hwp.InitScan()  # 탐색 재시작(문자열 수정하면 탐색 재시작해야 함)
            if re.match(rf"^제{장번호}장", text[1].replace(" ", "")):  # "제?장"을 찾았으면
                hwp.MovePos(201)  # 해당 라인 시작점으로 가서
                hwp_get_bold(hwp, "장")  # 위의 굵게함수 적용
                장번호 += 1  # 장-인덱스 + 1
                hwp.InitScan()  # 탐색 재시작
            else:  # 장도 절도 아니면
                pass  # 아무 것도 하지 말고 넘어가기
    hwp.ReleaseScan()  # while문 종료되었으면 탐색프로세스도 종료
    hwp.MovePos(2)  # 끝났으니까 문서 시작점으로 이동.  끝.


if __name__ == '__main__':  # 메인함수(필수는 아닌데, 뭔가 진입점 같은 게 간결해 보임?)
    root = Tk()  # GUI클래스 생성
    filename = askopenfilename() # HWP파일선택 다이얼로그 열기
    root.destroy()  # 파일 선택했으면 GUI 닫기

    hwp = hwp_init(filename=filename)  # 한/글 오브젝트 생성
    hwp.MovePos(2)  # 문서 시작점으로 이동한 후에
    hwp_find_and_get_bold(hwp)  # 위의 찾아가서 진하게 하는 함수 실행. 끝.

 

빈 줄과 주석 포함해서 100줄 정도 되네요.

"완전 간단하잖아!?"라고 환호하기는 애매한 길이의 코드지만,

코딩에 대한 깊은 이해가 필요하거나, 고난도의 문법이 들어간 스크립트가 아니기 때문에

스토리만 어느 정도 이해하신다면 코드도 술술 읽히실 겁니다. 특히 한/글에 익숙하시다면요.

대한민국 공무원이나 공공기관 직원 모두가 한/글 업무자동화에 능숙해질 필요는 절대 없다고 생각하지만,

딱 한 팀에 한 명, 한 처부에 한 명 정도만 IT경계인이 있어도, 팀이 강력한 아웃퍼포먼스를 발휘할 것으로 생각합니다.

이번 포스팅은 여기서 마칩니다.

다음 포스팅에서는 제?장 위아래에 빈 줄 삽입 및 중앙정렬, 각 조 위에 빈 줄 삽입하는 코드를 소개하겠습니다.

눈치채셨겠지만, 자꾸 써먹었던 함수 가지고 이것저것 뚝딱거리고 있습니다. (주로 찾아가기, 찾아바꾸기 정도?)

다음 포스팅도 결국 장이나 조항제목 찾아가서 중앙정렬 하고 엔터치는 식의 코드입니다. 이번 포스팅이랑 상당히 비슷해 보일 것 같긴 하네요... 그럼,

긴 글 읽어주셔서 감사합니다.

행복한 하루 되세요^^


다음 포스팅은...

 

[3/4] 정관문서 서식잡기(장이름 중앙정렬, 위아래 빈라인 삽입)

3번 질문============== 정관 제 1 장 총칙 제 1조 제 2조.. 제 3조... 제 2 장 주식과 주권 제 4조.... 제 5조.. 제 3 장 임원 제 6조 등과 같을 때. 장의 위치를 페이지 가운데로 위치하고 싶고, 또한 각 장

www.martinii.fun

 

댓글