한컴오피스의 한글파일(hwp)에서 텍스트 추출할 일이 생겼다.

대충 뒤져보니 대단하신 분 께서 한글문서 파서 라이브러리를 만드셨는데, 아직까지도 일부기능에 대해 개선작업을 진행중이신 것 같다.

뭐 표 라거나 그림파일 등에 대해서는 정상동작하지 않는 듯 하지만 나는 텍스트만 추출할 것이기 때문에 당장은 문제없이 사용가능할것으로 보인다.

 

자세한 지원범위는 이 분의 깃으로 들어가보면 될 듯.

https://github.com/neolord0/hwplib

 

GitHub - neolord0/hwplib: hwp library for java

hwp library for java. Contribute to neolord0/hwplib development by creating an account on GitHub.

github.com

아무튼, 대충 임포트하고 대충 써보기로 한다. 생각보다 텍스트 추출이 아주 잘 되어서 다행이다.

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/kr.dogfoot/hwplib -->
<dependency>
  <groupId>kr.dogfoot</groupId>
  <artifactId>hwplib</artifactId>
  <version>1.0.1</version>
</dependency>
cs

이렇게 잡아주고,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* hwplib hwp document text extraction example
*/
 
import kr.dogfoot.hwplib.object.HWPFile;
import kr.dogfoot.hwplib.reader.HWPReader;
import kr.dogfoot.hwplib.tool.textextractor.TextExtractMethod;
import kr.dogfoot.hwplib.tool.textextractor.TextExtractor;
 
// 중략
 
HWPFile hwpFile;
String hwpText;
try {
    hwpFile = HWPReader.fromFile("/Users/kimfish/DEV/java_workspace/"+"/test.hwp");
    hwpText = TextExtractor.extract(hwpFile, TextExtractMethod.InsertControlTextBetweenParagraphText);
 
    System.out.println("===== hwp text extractor =====");
    System.out.println("hwpText = " + hwpText);
catch (Exception e) {
    e.printStackTrace();
cs

이렇게 쓰면 된다. 개꿀

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[PDFBox] pdf 텍스트 추출  (0) 2022.07.05
[poi] java poi 를 활용한 엑셀/워드 텍스트 추출  (0) 2022.07.04
[Tomcat 9.x] JNDI설정  (0) 2022.02.21
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return (2)  (0) 2021.07.06
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

일전의 포스트와 마찬가지로 pdf 에서도 텍스트를 추출할 일이 생겼다.

당연하겠지만 해당 pdf는 ocr이 된 pdf를 기준으로만 추출이 가능하다.

 

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
<dependency>
  <groupId>org.apache.pdfbox</groupId>
  <artifactId>pdfbox</artifactId>
  <version>2.0.24</version>
</dependency>
cs

이야 1년쯤 전에는 2.0.18 이었는데 그새 버전업했네. 아무튼 maven repository는 이렇게 잡아주고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* PDFBox library PDF text Extraction Example
*/
 
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
 
// 중략
 
try {
    File file = new File("/Users/kimfish/DEV/java_workspace/"+"/test.pdf");
    PDDocument document;
    document = PDDocument.load(file);
 
    PDFTextStripper s = new PDFTextStripper();
    String content = s.getText(document);
 
    System.out.println("===== docx text extractor =====");
    System.out.println(content); 
catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
cs

이렇게 하면 정상적으로 OCR 처리된 text가 추출된다.

 

 

 

블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

아주 심플하게, 회사에서 해당 문서규격(xls/xlsx/doc/docx)에 대해 텍스트를 추출하는것이 필요했다. 일전에는 잠깐 해보았는데, 정리된 적이 없었고 이번을 계기로 좀 알게된 몇가지 사실들이 있기에 간단하게 기록해본다.

 

일단 메이븐에서 다음과 같이 라이브러리를 잡아준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-scratchpad</artifactId>
  <version>5.2.2</version>
</dependency>
 
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>5.2.2</version>
</dependency>
 
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>5.2.2</version>
</dependency>
 
cs

 

1. 워드 (doc/docx) 텍스트 추출

워드 2003 이전버전인 doc 와 이후 버전인 docx 를 추출하기 위해서는 두 가지만 기억하면 된다.

HWPFDocument 클래스는 Horrible Word Process Format 의 약자라는 점이며, poi 라이브러리만 상속받는것이 아닌 poi-scratchpad 라이브러리를 같이 상속받아야 한다. 즉, poi dependency만 잡아놓으면 HWPFDocument가 import 되지 않는다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* Word 2003 doc File Text Extraction Example
*/
 
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
// 중략
 
try {
    POIFSFileSystem poiFS = new POIFSFileSystem(
        new FileInputStream("/Users/kimfish/DEV/java_workspace/"+"/test.doc"));
    HWPFDocument hwp = new HWPFDocument(poiFS);
    WordExtractor we = new WordExtractor(hwp);
 
    String[] paragraphs = we.getParagraphText();
    System.out.println("===== doc text extractor ======");
    for (int i = 0; i < paragraphs.length; i++) {
        System.out.println(paragraphs[i]);
    }
 
catch (Exception e) {
    System.out.println(e); 
}
cs

 

docx 파일은 XWPFDocument를 import 받으며, docx의 text추출을 위해서는 poi 및 poi-ooxml 라이브러리에 대한 dependency가 필요하다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* Word 2007 docx File Text Extraction Example
*/
 
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
 
// 중략
 
try(
    FileInputStream fis = new FileInputStream
            ("/Users/kimfish/DEV/py_workspace/"+"/test.docx")) {  
    XWPFDocument file   = new XWPFDocument(OPCPackage.open(fis));  
    XWPFWordExtractor ext = new XWPFWordExtractor(file);  
 
    System.out.println("===== docx text extractor ======");
    System.out.println(ext.getText());  
}catch(Exception e) {  
    System.out.println(e);  
}  
cs

 

2. 엑셀 (xls/xlsx) 텍스트 추출

엑셀은 고려할 점이 상당히 많은데 각 셀/로우 별 텍스트 및 쉬트도 있는데다가 수식까지 가지고 있다. 현재는 샘플 토이 프로젝트라 수식이나 셀/로우 구분없이 텍스트만 가져오기를 수행해보았는데 일단 첫 쉬트에서는 잘 가져오고 있는듯. 좀 더 정교한 작업이 필요할 경우엔 공식 document 가 필요할것으로 보인다.

 

엑셀 2003 이전버전인 xls 같은 경우에는 poi Dependency 하나로도 잘 작동한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* Excel 2003 xls File Text Extraction Example
*/
 
import org.apache.poi.hssf.extractor.ExcelExtractor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
// 중략
 
try (
    FileInputStream fis = new FileInputStream("/Users/kimfish/DEV/java_workspace/"+"/test.xls")) {  
    
    String result = "";
 
    HSSFWorkbook wb = new HSSFWorkbook(fis);
    ExcelExtractor ee = new ExcelExtractor(wb);
    result = ee.getText();    
 
    System.out.println("===== xls text extractor =====");
    System.out.println(result);    
catch (Exception e) {
    //TODO: handle exception
}
cs

 

하지만 엑셀 2007 버전인 xlsx 같은 경우에는 워드2007과 마찬가지로 poi / poi-ooxml dependency 둘 다 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* Excel 2007 xlsx File Text Extraction Example
*/
 
import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
// 중략
 
try (
    
    FileInputStream fis = new FileInputStream("/Users/kimfish/DEV/py_workspace/"+"/test.xlsx")) {  
                
    // Workbook
    XSSFWorkbook wb = new XSSFWorkbook(fis);
            
    // Text Extraction
    XSSFExcelExtractor extractor = new XSSFExcelExtractor(wb);
    extractor.setFormulasNotResults(true);
    extractor.setIncludeSheetNames(false);
    
    System.out.println("===== xlsx text extractor =====");
    System.out.println( extractor.getText() );
catch (Exception e) {
    //TODO: handle exception
}
cs

 

일단은 이렇게 해서 정상적으로 출력되는것을 확인함. 어휴 poi 하나로 다 해놓든가 의존성을 걸어두던가. 왜 poi / poi-ooxml / poi-scratchpad 이렇게 세 개가 죄다 쪼개져있는지원;;

 

정리

xlsx - poi / poi-ooxml

xls - poi

docx - poi / poi-ooxml

doc - poi / poi-scratchpad

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[hwplib] hwplib을 이용한 한글파일 텍스트 추출  (0) 2022.07.06
[PDFBox] pdf 텍스트 추출  (0) 2022.07.05
[Tomcat 9.x] JNDI설정  (0) 2022.02.21
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return (2)  (0) 2021.07.06
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

파이썬 개발을 하면서 가장 헷갈리는 개념이 디렉토리 참조법이다. 자바에서는 그냥 대충 import package.directory.fileName; 과 같은 형식으로 지정해주면 바로 가져다 쓸 수 있는데 반해, 파이썬은 그렇지 않은가보다.

 

아무튼 세 가지의 케이스에 대해 정리/적용해보았고 그 내용은 다음과 같다.

 

패키지 구성은 다음과 같은 형식으로 되어있다고 가정한다.

project
⌞ A_folder
   - a_file_1.py
   - a_file_2.py
 B_folder
   - b_file_1.py
   ⌞ C_folder
       - c_file_1.py
   ⌞ D_folder
       - d_file_1.py

1. 동일 경로상의 import

A_folder 내부의 a_file_1.py 에서 a_file_2.py 파일을 import 하기위해서는 다음과 같이 작성한다.

1
from . import a_file_2.py
cs

또는

1
import a_file_2.py
cs

이거는 쉽다.

 

2. 하위 폴더의 import 

B_folder 내부의 b_file_1.py 에서 C_folder 내부의 c_file_1.py를 import 하기 위해서는 다음과 같이 작성한다.

1
from C_folder import c_file_1.py
cs

이정도는 크게 어렵지 않다.

 

3. 상위 폴더의 import

여기서부터 많이 헷갈리게 된다.

C_folder 내부의 c_file_1.py 에서 D_folder 내부의 d_file_1.py를 import 하기 위해서는 꽤 여러가지 방법이 있으나, 일단 내가 해결한 방법을 위주로 작성해본다.

1
2
3
4
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__))))
from D_folder import d_file_1.py
cs

여기에서 os.path.dirname(os.path.abspath( 는 한단계 상위를 의미하며, C_folder의 한단계 상위인 B_folder로 이동함을 의미한다. B_folder 내부에 D_folder가 위치해있으므로, from d_folder 을 해주는 것.

 

마찬가지로 c_file_1.py 에서 A_folder 를 참조하기 위해서는 os.path.dirname(os.path.abspath를 두 번 작성해주면 될 것이다.

 

 

java를 하다와서 그런지, from~ 쪽도 잘 이해가 안갔고 __file__과 같은 옵션네이밍(?)의 정의도 익숙하지 않다. 일단은 해결했으니까, 다음 개발로 넘어가야지...

블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

1
2
3
4
***** Usage Dev Env *****
= OS - macOS Monterey ver 12.3.1
= IDE - VSCode
= Lang - Python 3.10.4
cs

개발하고나서보니 아래의 내용 모두 다 무쓸모했다.

내가 크롤링하려던 사이트에서 스크립트의 오류인지 뭔지 chromedriver로는 정상적으로 로딩할 수 없었고, firefox를 이용한 geckodriver를 활용하니 단박에 모두 정리가 되었다.

 

아 크롬을 이렇게 버려야하나 ㅡㅡ;

파이썬으로 크롤링 개발을 하는데 요상한 에러가 발생했다. 사실 별 소스코드도 없이 파이썬에 셀레니움, 크롬드라이버를 붙여서 브라우저 하나 띄우는건데 여기서 저런 에러를 만날 줄이야 꿈에도 몰랐다.

 

대충 아래의 소스코드에서 오류가 발생했다.

1
2
3
4
5
6
7
8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
 
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=chrome_options)
driver.get('http://kimfish.co.kr')
driver.quit()
cs

 

그리고 발생오류상황은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  ~ /usr/local/bin/python3 /Users/kimfish/DEV/py_workspace/testChromeDriver.py
Traceback (most recent call last):
  File "/Users/kimfish/DEV/py_workspace/testChromeDriver.py", line 9, in <module>
    driver.get("https://bbs.ruliweb.com")
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 437, in get
    self.execute(Command.GET, {'url': url})
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 425, in execute
    self.error_handler.check_response(response)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/selenium/webdriver/remote/errorhandler.py", line 247, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: unknown error: session deleted because of page crash
from tab crashed
  (Session info: chrome=100.0.4896.88)
Stacktrace:
0   chromedriver                        0x0000000105383c34 chromedriver + 4406324
1   chromedriver                        0x000000010531d290 chromedriver + 3986064
2   chromedriver                        0x0000000104f7e71c chromedriver + 190236
=== 중략 ===
➜  ~ 
cs

 

Message: unknown error: session deleted because from tab crashed 와 같은 오류에 대해 검색을 해보니, 가상머신(대부분 도커)의 /dev/shm 메모리가 부족하다는둥 뭐라는둥 말이 많았는데 난 단순히 내 로컬환경에서 개발을 할 뿐이다 ㅠ

 

그래서 조금 더 찾아보니 chromedriver 호출시, 다음과 같은 옵션을 설정해주면 된다고 한다.

1
2
3
4
5
6
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--window-size=1420,1080')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--disable-gpu')
cs

적용 후 돌렸다! 그런데도 똑같은 오류가 발생한다!

 

환장하겄다 싶어 옵션을 다 빼보면서 테스트 하다가 결국 원인을 찾기는 했다. 바로, --headless 옵션이 문제였다.

해당 옵션은 UI가 지원되지 않는 서버들을 위해 작동하는 옵션으로 알고 있는데, 내 개발환경에서는 왜 오류가 발생하는지는 아직 잘 모르겠다. 일단 저 옵션을 제외하고는 잘 동작하니까 개발하겠지만... 조금 불안하다. 원인을 알면 해결법도 찾아야하는데.. 으으 

'어장 Develop' 카테고리의 다른 글

[Python] 디렉토리 참조법  (0) 2022.05.26
[Mac] chromedriver 설치부터  (1) 2022.04.10
[MacBook] M1 MacBookPro 에 openjdk설치  (0) 2022.04.09
[http1.1] Expect: 100-continue  (0) 2021.03.18
[tomcat] p12 ssl 적용  (0) 2020.06.08
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

* M1 MacBookPro macOS Monterey 12.3.1

 

파이썬 + selenium을 이용하여 웹 크롤러를 만드려고 한다.

그전에 셀레니움을 사용하기 위해서는 크롬 디버그모드 활용툴과 같은 개념인 chromedriver를 설치, 사용해야 한다. 설치법은 크게 두 종류로 나뉘는 것으로 보이는데, 'homebrew install'과 '공식홈페이지 다운로드 설치' 두가지의 방법이다.

 

개인적으로는 버전관리가 간편한 homebrew 설치가 좋을것이라 생각한다. 괜히 공식 홈페이지 인스톨 했다가 크롬브라우저 버전업 하면 또 가서 다운받고 압축풀고 할라면 귀찮을듯.

1.  homebrew 로 설치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
➜  ~ brew install cask chromedriver
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> New Formulae
gops
==> Updated Formulae
Updated 2 formulae.
 
==> Downloading https://chromedriver.storage.googleapis.com/100.0.4896.60/chrome
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/366342d76a11f7c61ca4b54b8d00cf63f59917eb118f2d9f5745484daa0f8598--chromedriver_mac64_m1.zip
==> Installing Cask chromedriver
==> Linking Binary 'chromedriver' to '/opt/homebrew/bin/chromedriver'
🍺  chromedriver was successfully installed!
==> Downloading https://ghcr.io/v2/homebrew/core/cask/manifests/0.8.8
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/2b8b007815bfe6179d48bcdcbcce75cc494fd12d6c3dd831e60a05cb50229c7f--cask-0.8.8.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/cask/blobs/sha256:f50a59d4337bc
Already downloaded: /Users/kimfish/Library/Caches/Homebrew/downloads/10cb17e2b53cf4b92c88efd42c0d6dcaf43570a3e7ba65939e070cb79335b3bd--cask--0.8.8.all.bottle.tar.gz
==> Pouring cask--0.8.8.all.bottle.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /opt/homebrew/share/emacs/site-lisp/cask
==> Summary
🍺  /opt/homebrew/Cellar/cask/0.8.813 files, 152KB
==> Running `brew cleanup cask`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Caveats
==> cask
Emacs Lisp files have been installed to:
  /opt/homebrew/share/emacs/site-lisp/cask
cs

한 번 지웠다가 재설치한 부분이 있어 로그가 좀 깔끔하지 않은듯 한데, 일단 설치 자체는 어렵지 않다.

그리고 이거 다른 사람들의 블로그에서 잘 다뤄주지 않던 이야기인데, 크롬 브라우저는 워낙에 버전업이 자주되다보니 소스코드가 잘 안돌아간다 싶으면 수시로 브라우저 버전과 크롬드라이버 버전이 일치하는지 확인해야 한다. 그럴 때 쓰는 버전 확인 명령어는 다음과 같다.

 

2. chromedriver 악성소프트웨어 검사

어라? 크롬드라이버 버전확인을 하려 했더니 다음과 같은 오류메시지가 발생하네?

 

‘chromedriver’() Apple에서 악성 소프트웨어가 있는지 확인할 없기 때문에 없습니다.

이는 권한 설정으로 해결할 수 있다. 다음과 같이 수행이 가능하다.

1
2
3
➜  bin git:(stable) pwd
/opt/homebrew/bin
➜  bin git:(stable) xattr -d com.apple.quarantine chromedriver
cs

주의할 점은 xattr -d 명령어 자체가 chromedriver 가 설치되어있는 디렉토리에서 수행해야 한다는 점이다.

brew 로 설치하는 경우에는 /opt/homebrew/bin 디렉토리 내부에 위치한다.

정상적으로 적용이 되어도 별다른 프롬프트가 나오지 않으니 당황하지 말고, 바로 버전 확인 명령어를 작성한다.

1
2
3
4
➜  bin git:(stable) pwd
/opt/homebrew/bin
➜  bin git:(stable) chromedriver -version
ChromeDriver 100.0.4896.60 (6a5d10861ce8de5fce22564658033b43cb7de047-refs/branch-heads/4896@{#875})
cs

현재 브라우저의 버전과 크롬드라이버 버전이 일치하는지는 이렇게 확인하면 된다.

블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

  • cyberjam 2022.09.25 20:35  댓글주소  수정/삭제  댓글쓰기

    잘 봤습니다.

    저는 해당 명령어로 설치했습니다.
    brew install --cask chromedriver
    https://formulae.brew.sh/cask/chromedriver#default

얼마전에 맥북을 샀다. 그러면서 새롭게 개발환경을 설정했다. 이하는 OpenJdk를 설치하는 내용이다.

명령어 순서는 다음과 같다.

1
2
3
4
5
6
7
1. brew tap AdoptOpenJdk/openjdk
 
2. brew install --cask adoptopenjdk8
 
3. /usr/libexec/java_home -1.8
 
4. java -version
cs

 

1. brew tap AdoptOpenJdk/openjdk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kimfish@KimFish-MacBookPro ~ % brew tap AdoptOpenJdk/openjdk
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
Updated 4 formulae.
==> Tapping adoptopenjdk/openjdk
Cloning into '/opt/homebrew/Library/Taps/adoptopenjdk/homebrew-openjdk'...
remote: Enumerating objects: 1996done.
remote: Counting objects: 100% (60/60), done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 1996 (delta 44), reused 49 (delta 38), pack-reused 1936
Receiving objects: 100% (1996/1996), 372.27 KiB | 7.16 MiB/s, done.
Resolving deltas: 100% (1424/1424), done.
Tapped 47 casks (69 files, 521.8KB).
 

 

2. brew install --cask adoptopenjdk8 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kimfish@KimFish-MacBookPro ~ % brew install --cask adoptopenjdk8
==> Downloading https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/downl
==> Downloading from https://objects.githubusercontent.com/github-production-rel
######################################################################## 100.0%
==> Installing Cask adoptopenjdk8
==> Running installer for adoptopenjdk8; your password may be necessary.
Package installers may write to any location; options such as `--appdir` are ignored.
installer: Package name is AdoptOpenJDK
installer: Installing at base path /
installer: The install was successful.
package-id: net.adoptopenjdk.8.jdk
version: 1.8.0_292-b10
volume: /
location: 
install-time: 1649472633
🍺  adoptopenjdk8 was successfully installed!
cs

 

3.  /usr/libexec/java_home -v 1.8

1
2
kimfish@KimFish-MacBookPro ~ % /usr/libexec/java_home -1.8
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
cs

 

4. java -version

1
2
3
4
kimfish@KimFish-MacBookPro ~ % java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.292-b10, mixed mode)
cs

 

정상적으로 설치가 된 모습이다. 설치하고나니 AdoptOpenJdk가 아닌 RedhatOpenJdk를 설치할걸 그랬나 싶긴 하지만, 일단 사용 해 보고 문제가 발생하면 나중에 다시 설치하지뭐

블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

뭔놈의 플젝이 SVN Update를 받으면 계속 에러가 나는건지. 이번에는 누가 JNDI 설정을 갈아치운건지 에러가 난다. 그래서 정리할 겸 작성함.

해당 프로젝트는 Tomcat 9.0버전대를 기준으로 JNDI설정이 되어있음.

 

1. Project - application.yml

1
2
3
spring:
  datasource:
  jndi-name: TESTDB_Pool
cs

별거는 없다. SpringBoot의 Application.yml 에서 JNDI를 다음과 같은 이름으로 사용하겠다고 명시적으로 알려주면 끝.

 

2. Tomcat - Context.xml

1
2
3
4
5
6
//중략
 
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
 
//application.yml에 작성한 spring.datasource.jndi-name과 동일하게 작성한
<ResourceLink global="TESTDB_Pool" name="TESTDB_Pool" type="javax.sql.Datasource"/>
cs

여기 또한 별거 없다. application.yml의 jndi-name과 3.Tomcat-Server.xml과 연결시켜주는 부분이다. 

 

3. Tomcat - Server.xml

1
2
3
4
5
6
7
8
9
10
11
//중략
<Resource auth="Container" description="User database that can be updated and saved"
  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
  name="UserDatabase" pathname="conf/tomcat-users.xml"
  type="org.apache.catalina.UserDatabase" />
 
//하단에 작성
<Resource auth="Container" driverClassName="com.tmax.tibero.jdbc.TbDriver"
  maxActive="5" maxIdle="5" name="TESTDB_Pool" type="javax.sql.DataSource"
  url="jdbc:tibero:thin:@000.000.000.000:65535" username="DB_ID" password="DB_PASSWORD"
  validationQuery="select 1 from dual" />
cs

가장 중요한 부분이다. DB커넥션에 관련된 모든 정보를 작성하는 곳으로, 기존의 application.yml에서 작성된 DB정보를 여기에 작성한다고 생각하면 된다.

 

 

이렇게 설정하고 재부팅 하면 된다.

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[PDFBox] pdf 텍스트 추출  (0) 2022.07.05
[poi] java poi 를 활용한 엑셀/워드 텍스트 추출  (0) 2022.07.04
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return (2)  (0) 2021.07.06
[xml] RestAPI XML Return  (0) 2021.07.02
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

플젝을 하면서 PDF 문서만들기를 하게 되었다. 주어진 이미지들을 하나의 PDF 문서로 만드는 것인데 구글링 하니 잔뜩 나오고해서 내 입맛대로 소스 변경 후 기록용으로 작성한다.

 

우선 사용하는 라이브러리는 PDFBox라는 놈이다. Maven Dependency는 다음과 같다.

1
2
3
4
5
6
<!-- Apache PDF Box -->
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.18</version>
</dependency>
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public static String makeImgPdf(File[] imgDir , String pdfDir) throws Exception {
    String result = "";
    //PDF 문서를 생성함
    PDDocument doc = new PDDocument();
    try {
        //Post를 통해 생성할 이미지를 가져옴
        File[] imgFiles = imgDir;
        
        //첨부된 이미지 파일 갯수만큼 반복문 실행
        for ( int i = 0; i<imgFiles.length; i++) {
        //이미지 사이즈 확인
        Image img = ImageIO.read(imgFiles[i]);
        
      //PDF 페이지를 생성함
       PDPage page = new PDPage(PDRectangle.A4);   
       // 그래서 PDF 문서내에 삽입함
        doc.addPage(page);
         
        //PDF에 삽입할 이미지를 가져온다. 인자값 - 이미지경로, PDF 페이지
        PDImageXObject pdImage = PDImageXObject.createFromFile(imgFiles[i].toString(), doc);
        //현재 설정된 PDF 페이지의 가로/세로 구하기
        int pageWidth = Math.round(page.getCropBox().getWidth());
        int pageHeight = Math.round(page.getCropBox().getHeight());
        
        //이미지 가로사이즈가 PDF 가로사이즈보다 클 경우를 대비해서 이미지 리사이징 실행
        //현재 설정된 PDF 페이지 가로 , 이미지 가로 사이즈로 비율 측정 
        float imgRatio = 1;
        if ( pageWidth < img.getWidth(null)) {
            imgRatio = (float)img.getWidth(null/ (float)pageWidth;
            System.out.println(">>> 이미지 비율 : " + ratio);
        }
        
        //설정된 비율로 이미지 리사이징
        int imgWidth = Math.round(img.getWidth(null/ imgRatio);
        int imgHeight = Math.round(img.getHeight(null/ imgRatio);
        
        //이미지를 가운데 정렬하기 위해 좌표 설정
        int pageWidthPosition = (pageWidth - imgWidth) / 2;
        int pageHeightPosition = (pageWidth - imgWidth) / 2;
        
        PDPageContentStream contents = new PDPageContentStream(doc, page);
        //그 콘텐츠에 이미지를 그린다. 인자값 - X/Y/가로사이즈/세로사이즈
        contents.drawImage(pdImage, pageWidthPosition, pageHeightPosition, imgWidth, imgHeight);
        //콘텐츠에 이미지를 다 그렸으면 콘텐츠를 종료
        contents.close();
        //그린것이 끝났으니 해당 문서를 저장.
        doc.save(pdfDir);
        
 
        }
    } catch (Exception e) {
        System.out.println("Exception! : " + e.getMessage());
    }
 
    try {
        doc.close();
        result = "success";
    } catch (IOException e) {
        result = "error";
        e.printStackTrace();
    }
 
    return result;
}
cs

최대한 주석을 많이 달아놨다.

 

PDF를 만들면서 가장 신경쓴 부분이 이미지의 리사이징 및 가운데정렬 부분이었다.

27번 라인부터 시작하는데, PDF의 가로 길이와 이미지의 가로길이를 나눠준 비율만큼 이미지 세로 비율도 축소시켜버리는 것.

그리고 그렇게 축소된 이미지를 PDF의 가운데정렬을 해야했기 때문에 38번 라인에서 PDF의 가로만큼 이미지 가로 차이를 계산해준다. 그리고 반으로 나눠주면 PDF 내 이미지가 양쪽 여백이 계산될 것이다. 마찬가지로 PDF의 세로만큼 이미지 세로 차이를 계산한 후, 반으로 나눠주면 PDF 내 이미지의 상하 여백이 계산되는 방식.

 

일단은 전체적으로 잘 나온다.

추후에 고려할 부분은 옵션에 따라 PDF 용지의 가로방향/세로방향을 설정할 수 있게 하는 부분인데, 이 또한 크게 어렵지 않으니 금방할 수 있을 것으로 보인다.

 

+결과

PDF의 배경이미지 사이즈는 가로 595, 세로 842이다

이미지는 가로 3000, 세로 3600으로, 계산시 이미지 비율이 약 5.042017이 나온다.

즉, 이미지 축소가 없었으면 PDF 전체를 덮고도 남았을것이나, PDF 가로 비율에 맞춰 축소가 성공적으로 진행되었으며,

위의 로직과 같이 상하 여백이 동일하게 되어 가운데 정렬이 성공적으로 되었음을 알 수 있다.

 

1
2
3
4
5
>>> pageWidth : 595
>>> pageHeight : 842
>>> 이미지 비율 : 5.042017
>>> imgWidth : 3000
>>> imgHeight : 3900
cs

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[poi] java poi 를 활용한 엑셀/워드 텍스트 추출  (0) 2022.07.04
[Tomcat 9.x] JNDI설정  (0) 2022.02.21
[xml] RestAPI XML Return (2)  (0) 2021.07.06
[xml] RestAPI XML Return  (0) 2021.07.02
[SpringBoot] Async 사용하기  (0) 2021.06.25
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요

지난번에 XML RestAPI Return에 대해 포스팅을 하자마자, 실제 URL 호출 테스트를 해보니 웬걸 내가 전달받은 형태와는 판이하게 달랐다.

 

지난번에 작성한 부분은 아래와 같고, 그것에 대한 소스코드는 여기로 들어가면 된다.

https://kimfish.co.kr/328

1
2
3
4
5
6
7
<MAIN>
  <AGE>16</AGE>
  <SEX>Male</SEX>
  <JOB>Student</JOB>
  <PHONE>01022223333</PHONE>
  <STATUS>Normal</STATUS>
</MAIN>
cs

여튼, 이번에 서버와 통신을 해보니 다음과 같은 형식으로 이루어져야만 했다.

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="EUC-KR" standalone="no"?>
<MAIN>
  <AGE value="16"/>
  <SEX value="Male"/>
  <JOB value="Student"/>
  <PHONE value="01011112222"/>
  <STATUS value="Normal"/>
</MAIN>
cs

허미 ==;

여튼 그래서 다음과 같이 작성했다. 지난번의 경우에는 jackson-dataformat-xml 라이브러리를 활용했으나, 이번에는 기본적인 DocumentBuilderFactory를 활용하는걸로..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.io.StringWriter;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Element
import org.w3c.dom.Document;
import org.w3c.dom.Attr;
 
 
@GetMapping(path="/Main", produces=MediaType.APPLICATION_XML_VALUE)
public String xmlTestMain(HttpServletRequest request) throws Exception{
        Document doc = null;
        DocumentBuilderFactory factory = null;
        factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        doc = builder.newDocument();
        
        //최상위 Root를 구성하는 Element 생성
        Element rootElement = doc.createElement("MAIN");
        doc.appendChild(rootElement); 
 
        //하위 Element를 생성. 
        Element ageElement = doc.createElement("AGE");
        //Attribute 를 신규로 선언해주고, Document를 이용하여 value를 생성함
        Attr attr = doc.createAttribute("value");
        //생성된 Attr에 value를 세팅
        attr.setValue("1");
        //생성한 ageElement에 위에서 신규선언된 Attr을 세팅
        ageElement.setAttributeNode(attr);
        // ageElement를 rootElement의 하위요소로 지정
        rootElement.appendChild(ageElement);
 
        //위의 과정을 아래와 같이 단순화 시킬 수 있음
        Element sexElement= doc.createElement("SEX");
        sexElement.setAttribute("value""Male");
        rootElement.appendChild(sexElement);
 
        Element jobElement = doc.createElement("JOB");
        jobElement .setAttribute("value""Student");
        rootElement.appendChild(jobElement );
 
        Element phoneElement = doc.createElement("PHONE");
        phoneElement .setAttribute("value""01011112222");
        rootElement.appendChild(phoneElement );
 
        Element statusElement = doc.createElement("STATUS");
        statusElement .setAttribute("value""Normal");
        rootElement.appendChild(statusElement );
 
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "euc-kr");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        doc.setXmlStandalone(false);
 
        StringWriter sw = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(sw));
 
        String aaa = sw.getBuffer().toString();
        return aaa;
}
 
cs

에휴 일단 됐다... 좀 쉬자

'어장 Develop > 어장 JAVA' 카테고리의 다른 글

[Tomcat 9.x] JNDI설정  (0) 2022.02.21
[PDFBox] java Image to PDF  (0) 2021.08.24
[xml] RestAPI XML Return  (0) 2021.07.02
[SpringBoot] Async 사용하기  (0) 2021.06.25
[SpringBoot] fixed delay, fixed rate의 차이  (0) 2021.06.23
블로그 이미지

김생선

세상의 모든것을 어장관리

댓글을 달아 주세요