HTML TO PDF

시작하면서

Excel을 PDF로 저장하기.

저를 하루 종일 괴롭힌 그것 "Excel을 PDF로 바꿔서 블록체인에 저장"이 목표였습니다.
구글검색 결과 바로 바꾸는건 쉽지 않다고 생각하고 HTML을 바꾼뒤에 다시 PDF로 써야겠다는 생각을 하게 됩니다. 그러나 많은 사람들이 겪은 문제를 우리도 겪게되죠.
  • 한글은 변환이 안됨(폰트때문인듯)
  • html을 xhtml로 전환이 잘 안됨
  • 글자는 되는데 라인은 변환이 안됨

Phantomjs

앗~ 이거 node js네.. 아 귀찮아 서버에 node깔고 어쩌고 저꺼고 또 통신할려면 rest api 사용해야 하고 .....

하지만 그 후로도 마땅한 방법을 찾지 못해 구글 신에게 다시 물어물어 Phantomjs를 홈페이지 를 갑니다. 

어~ download 이런 이미 OS별로 실행파일이 다 만들어져있었습니다.

자 그럼 아주 간단한 사용법만 알아보겠습니다.

PDF 2 HTML Sample


구글 검색 결과 아주 간단히 변경이 가능합니다.
검색 결과 페이지 : https://coderwall.com/p/5vmo1g/use-phantomjs-to-create-pdfs-from-html
요렇게만 하면 끝이라네요

phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf

그래서 테스트

phantomjs rasterize.js "http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes" jakarta.pdf
Can't open 'rasterize.js'
Can't open 'rasterize.js'
실패



rasterize.js 파일은 받은 파일에 examples 폴더에 있습니다.

이제 다시 아래와 같이 우리가 만든 HTML 파일 변경을 시도합니다.

phantomjs ..\examples\rasterize.js W:\sample.html W:\sample5.pdf
Unable to load the address!
Unable to load the address!
실패
아주 잠시 고민 브라우저 창의 파일 주소를 생각해냈어요

phantomjs ..\examples\rasterize.js file:///W:/sample.html W:\sample5.pdf
넘나 중요한거 "file:///" 여기보면 "/" 가 3개라는거 꼭 기억하세요




성공

이제 코드에 적용해 보면 되겠네요~~




추가


리눅스에서 사용 방법 입니다.

우선적으로 설치후에 사용하려 하면 폰트관련 에러가 발생합니다.
( phantomjs: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory )

아래에서 2~3번을 실행하면 관련 에러는 발생하지 않지만 한글이 깨지는 이슈가 있습니다.

한글 폰트깨짐을 해결하기 위해 4~10번의 과정을 해주면 한글도 잘 나오는걸 확인할 수 있습니다.
  1. tar -xvf phantomjs-2.1.1-linux-x86_64.tar.bz2
  2. yum install libfontconfig.so.1
  3. yum install fontconfig
  4. yum install fonts-korean
  5. yum install cjkuni-fonts-common cjkuni-ukai-fonts cjkuni-uming-fonts
  6. yum install libXext libXrender xorg-x11-fonts-Type1 xorg-x11-fonts-75dpi
  7. fc-cache -f -v
  8. wget http://cdn.naver.com/naver/NanumFont/fontfiles/NanumFont_TTF_ALL.zip
  9. mkdir /usr/share/fonts/nanumfont
  10. unzip NanumFont_TTF_ALL.zip -d /usr/share/fonts/nanumfont
  11. rm -f /usr/share/fonts/nanumfont/NanumFont_TTF_ALL.zip
  12. rm -f /usr/share/fonts/nanumfont/NanumBrush.ttf
  13. rm -f /usr/share/fonts/nanumfont/NanumPen.ttf


블로그 이미지

B급 감성

,

셀레니움을 통한 웹 테스트 준비 스크립트


드라이버 가져오기

ChromeDriver

get_chromedriver.sh
#!/bin/bash
os_name=`uname`
chromedriver_dir="chromedriver"
if [ ! -d $chromedriver_dir ]; then
mkdir $chromedriver_dir
fi

echo "downloading chromedriver"

if [[ $os_name == 'Linux' && ! -f $chromedriver_dir/chromedriver ]]; then
cd chromedriver && curl -L https://chromedriver.storage.googleapis.com/2.29/chromedriver_linux64.zip > tmp.zip && unzip -o tmp.zip && rm tmp.zip
# wait until download finish
sleep 5
elif [[ $os_name == 'Darwin' && ! -f $chromedriver_dir/chromedriver ]]; then
cd chromedriver && curl -L https://chromedriver.storage.googleapis.com/2.29/chromedriver_mac64.zip | tar xz
sleep 5
fi

GeckoDriver

get_geckdriver.sh
#!/bin/bash
os_name=`uname`

if [ -f geckodriver ]; then
exit 0
fi
echo "downloading gechdriver"

if [[ $os_name == 'Linux' ]]; then
cd ../ && curl -L https://github.com/mozilla/geckodriver/releases/download/v0.11.1/geckodriver-v0.11.1-linux64.tar.gz | tar xz
sleep 5
elif [[ $os_name == 'Darwin' ]]; then
cd ../ && curl -L https://github.com/mozilla/geckodriver/releases/download/v0.11.1/geckodriver-v0.11.1-macos.tar.gz | tar xz
sleep 5
fi

Selenium

Selenium 가져오기

get_selenium.sh
#!/bin/bash

#check if selenium server is up running
pid=`lsof -ti tcp:4444`
if [ $? -eq 0 ]
then
kill -9 $pid
fi
java -jar -Dwebdriver.gecko.driver=../geckodriver -Dwebdriver.chrome.driver="chromedriver/chromedriver" ../webdriverio-test/selenium-server-standalone-3.0.1.jar &

환경설정

Linux

setup_linux_env.sh
#!/bin/bash

if [ "${TRAVIS_OS_NAME}" == "linux" ]
then
export CHROME_BIN="/usr/bin/google-chrome"
export DISPLAY=:99.0
sh -e /etc/init.d/xvfb start &
fi

OSX

setup_osx_env.sh
#!/bin/bash
if [ "${TRAVIS_OS_NAME}" == "osx" ]
then
brew cask install google-chrome
sudo Xvfb :99 -ac -screen 0 1024x768x8 &
export CHROME_BIN="/Applications/Google Chrome.app"
fi

테스트환경 검사

test_setup.sh
#!/bin/bash

EXIT_STATUS=0

function check_command {
"$@"
local STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "error with $1 ($STATUS)" >&2
EXIT_STATUS=$STATUS
fi
}

check_command tests/scripts/get_geckdriver.sh
sleep 5
check_command tests/scripts/get_selenium.sh
sleep 5
check_command tests/scripts/get_chromedriver.sh
sleep 10
check_command tests/scripts/selenium_connect.sh
sleep 10

exit $EXIT_STATUS


블로그 이미지

B급 감성

,


Javascript XPath를 이용해서 Dom Element 가져오기


function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

console.log( getElementByXpath("//html[1]/body[1]/div[1]") );


블로그 이미지

B급 감성

,

Spring-boot RestAPI 와 Spring Security OAuth2 연동(spring-boot 1.5.10 기준)

Dependency 추가

Gradle

dependencies {
...
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.security.oauth:spring-security-oauth2')
...
}

Maven

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>

인증URL 설정

ResourceServerConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and()
.authorizeRequests()
.antMatchers("/swagger-resources/**","/swagger-ui.html",
"/v2/api-docs", "/webjars/**").permitAll() // Swagger Support
.antMatchers("/", "/api/public-key", "/error").permitAll()
.anyRequest().authenticated()
;
}
}
@EnableResourceServer 어노테이션을 추가한 클래스가 만들어지면 기본적으로 모든요청에 대해 인증을 필요로하게 된다.
특정 url에 대해서 인증을 필요로하지 않을 경우 위와같이하면 permitAll 을 제외한 모든 요청에 인증이 필요하다는 설정이고
또는 특정 url에 대해서만 인증이 필요하다면 permitAll과 authenticated 호출하는 부분의 순서를 바꿔주면 된다.

properties 추가

application.yml

security.oauth2:
client.client-id: foo
client.client-secret: bar
resource.token-info-uri: http://192.168.88.133:8780/oauth/check_token
auth-server-uri: http://192.168.88.133:8780
token요청 및 검증을 위해 인증서버의 tokin-info-uri와 client id/secret 등 설정이 추가되어야함

Swagger 관련 설정

SwaggerConfig.java

@Value("${security.oauth2.auth-server-uri}")
private String authServer;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;


@Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)
....
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(securityContexts())
....
;
}

@Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(clientId)
.clientSecret(clientSecret)
.scopeSeparator(",")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}

private SecurityScheme securityScheme() {
GrantType grantType = new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(authServer + "/oauth/token", "oauthtoken"))
.tokenRequestEndpoint(
new TokenRequestEndpoint(authServer + "/oauth/authorize", clientId, clientSecret))
.build();
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations")
};
return scopes;
}

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        String paths[] = {
                "/api/dashboard.*",
                "/api/metric-statistics.*"
        };

        for (String path: paths) {
            securityContexts.add(SecurityContext.builder()
                    .securityReferences(Arrays.asList(new SecurityReference("spring_oauth", scopes())))
                    .forPaths(PathSelectors.regex(path))
                    .build());
        }

        return securityContexts;
    }
Oauth서버의 해당클라이언트에 허용가능한 scope와 인증이 필요한 API Path등을 Swagger에 적용한다


블로그 이미지

B급 감성

,

Office - PDF export 용량 줄이기

아주 간단합니다. SaveAs( 다른이름으로 저장하기)에서 웹게시용만 선택하면 끝~~~

0



파일 --> 다른 이름으로 저장 --> [원하는 위치]
or
Ctrl + Shift + s
1


파일형식 - pdf
최소 크기(온라인 게시) 선택
2


조금 시간이 걸려요 ......
3


짜짠~~~ 25MB --> 3MB

끝~

P.S : PDF 말고 그냥 용량 줄이기

다른 이름으로 저장 에서
우측 하단에 "도구" --> 그림 압축
- 웹 또는 전자메일 정도 선택
- 잘려진 그림 영역 삭제 체크
4



블로그 이미지

B급 감성

,

Wandrd prvke 21L 여행용 백팩 내가 쓰는 방법





최대한 여행할때 들고다니는 세팅으로 구성했습니다.

prvke 21L 착용 샷









가방 외부


큐브 덕분인지 똑바로 잘 서있네요~




가방 오픈한 모습

큐브와 상단 부분



노트북 넣는 부분



매쉬소재로 되어있는 곳이 상단부분과 연결되는 부분입니다.

짐벌이 들어갈 공간입니다. 

보시다시피 짐벌이 한번에 쏙! 하고 들어가지는 않고 머리통 먼저 넣고 살짝 돌려서 넣어야 합니다.







이부분은 사이드 상단에 있는 포켓!

짐벌과 카메라 배터리, 렌즈 캡을 넣고 다닙니다




홈페이지에서는 선그라스 넣는 곳으로 소개하고 있는 부분.

해외여행 말고는 선그라스 낄 일이 없어서 저렇게 넣고 다닙니다.

생각보다 많이 들어갑니다. 저거 다 넣고도 더 들어갑니다 ㅋㅋ






여권 넣는 부분이라고 소개되는 포켓

해외여행 말고는 크게 필요를 못느낄 것 같습니다.




짐벌 손잡이와 미니 삼각대는 보통 이렇게 넣고 다닙니다.

촬영 시작하기 전이나 완전히 끝난 후에는 분리해서 넣고 다닙니다.





가방 내부


상단 부분을 열면 저만큼의 깊이 입니다.

사진으로 보면 잘 느껴지지 않아서 설명하자면

짐벌 넣고 확장하지 않으면 공간이 크게 남지는 않습니다.

그래서 31L 를 추천합니다. 



큐브 공간은 이정도 입니다.

크기 감 오시라고 일부러 담배 넣어놨습니다 ㅋㅋ


저렇게 세로는 담배 2갑정도의 크기이고 가로는 3갑정도 되려나.. 조금 더 될 것 같습니다.


a6500과 18-105 , 스몰리그 케이지 장착 , 배터리 충전기, 샤오미 보조배터리 보통 저렇게 넣어놓고 다닙니다.


단렌즈를 사게 될지 모르겠지만..단렌즈를 사더라도 충분히 공간은 있습니다.








마지막으로! 어찌보면 제일 중요할 수 있는 가격!

아마존에서 구매했으며 refund 를 받았습니다.


물품 : prvke21L 백팩 + 모든 악세서리


물품가격 : 264달러


배송비 & 포장 : 17.34달러


물품 + 배송비 금액 : 281.34달러


import fees deposit : 아마존에서 예상되는 관세를 미리 받는 것.

77.65 달러를 deposit 으로 걸어두었다.


그래서 결제 당시 $281.34 + $77.65 = $358.99 결제했다


후에 deposit으로 걸어두었던 $77.65에서 $50.82를 환불 받았다.


고로 결론은! $358.99 - $50.82 = $308.17가 관세를 포함한 총 가격이다.





이정도로 어느정도는 궁금증이 해결되시길 바라며!


댓글과 공감(❤) 클릭은 더 좋은 글을 위한 응원이 됩니다.

블로그 이미지

B급 감성

,