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급 감성

,


이제 막 블로그를 시작해보려고 하던 찰나에 회사에서 technical writing 에 대해서 설명해주는 기회가 있어서 듣게 되었다.


매주 한두시간씩 진행 할 예정이고 그때마다 정리해서 올려보려고 한다.


일단 Technical writing 의 개념부터 알아보기 전에 Technical communication 에 대해 알아보자.


위키에 잘 나와있지만 다 읽어보기 힘드니까 요약하자면


Technical communication ? 



Technical communication is a method of researching and creating information about echnical process or products directed to audience through media. 


미디어를 통해 사람들한테 전달되는 technical process 나 제품에 대한 정보를 조사하고 생성하는 방법.


내가 이해한 느낌은 Technical communicator 가 각종 미디어를 통해 Tehchnical information 을 전달하는 것을 통 틀어서 Technical communication 이라고 이해했다.




Technical writing ? 


기술문서 작성 이라고하면 당장 와닿지가 않는다.

카메라 메뉴얼, 세탁기 작동법 등등 넓은 범주가 있다. 


Technical communication 은 일방통행이지만 Technical writing 은 상호작용이 가능하다.



우리나라에서 Technical writing 이 중요해짐에 따라서 이제는 하나의 industry 로 분류되고 있다고 한다.



여기까지가 서두였고 이제 본론으로 들어가보자.






테크니컬 블로그 컨텐츠 작성 5개의 팁


  1.  Write it with a high-schooler in mind (고등학생을 염두에 두고 글을 작성해라)
  2.  Use analogies and examples ( 유추와 예제를 사용해라 )
  3.  Keep it simple and include links to more information ( 간결하게쓰며 자세한 사항은 링크를 사용한다. )
  4.  Format for easy reading  ( 읽기 쉬운 서식을 이용한다. )
  5.  Use expressive language ( 설명하듯이 쓴다. )





Write it with a high-schooler in mind (고등학생을 염두에 두고 글을 작성해라)


  1. 전문적인 영역, 긴글을 지양하고  단순,명료한 문장으로 글을 작성해라.
  2. 글쓴이가 생각하는 독자와 실제 독자의 괴리가 있다.

내 블로그에 높은 수준의 전문적인 훌륭한 글( 물론 없지만 )을 어떤 엔지니어가 읽고 마지막 결재자에게 내 글을 전달하는데 문제가 있을 것이다. 그 결재자는 전문용어를 알아듣지 못할 것이기 때문이다.. 

그래서 고등학생에게 알려준다는 마인드로 글을 작성하라는 것이다.





Use analogies and examples ( 유추와 예제를 사용해라 )


  1. 예를 들어라.
  2. 유추법을 사용해라.
내가 쓰고 있는 주제와 독자가 연관될 수 있는 유사점을 만들어 내야 한다.
적절한 유추법의 사용은 읽는이가 사물을 바라보는 새로운 관점을 제시해 준다.
예를 드는 것은 지금 이 글에서도 몇몇 부분은 예를 들어서 설명한 부분이 있다.



Keep it simple and include links to more information

 ( 간결하게쓰며 자세한 사항은 링크를 사용한다. )


  1. 블로그의 길이 조절하기
  2. 링크를 사용해라

너무 길다면 흥미를 빨리 잃을것이다.

한번 앉아서 읽을 때 한번에 슉 읽어낼 수 있는 정도.

링크를 사용하면 기술적으로 보이고, 내 블로그에 들어오면 좋은 곳으로 연결된다는 이미지를 느껴지게 할 수 있다.





Format for easy reading  ( 읽기 쉬운 서식을 이용한다. )


  1. 기술 블로그 일수록 요점이 한눈에 들어오도록 시작적으로 구조를 정해라.
  2. 주제목과 부제목의 내용이 부합되게 정해라.
  3. bullet 과 숫자는 각 주제를 부각시키고 순서를 분명히 해준다.
  4. 여백을 이용하여 눈에 피로감을 덜고 중요한 부분을 부각시킨다.
  5. 단락은 짧게해라.





Use expressive language ( 설명하듯이 쓴다. )


  1. 소설을 쓰는 느낌으로 블로그 포스트를 써라
  2. 기술 관련 블로그를 쓰면 다조로울 수 있지만 감정을 넣는다면 주의를 끌고 활기찬 글이 될 수 있다.
  3. 감정이 들어간 말들을 적절히 사용하면 지루한 주제도 살릴 수 있다.
예를들면, 향상된 속도로 인한 조작의 용이성 vs 부드러운 조작감





이 글을 쓰면서도 지키지 못한것들이 굉장히 많다.
단순히 이렇게 한번 슥 읽고 지나갈 것이 아니라 자주 보고 쓰면서 몸에 체득을 하는것이 빠를 것 같다. 


워낙 내가 설명충이라 짧게 쓰고 말아도 될 것들을 길게 풀어쓰는 경향이 있다.
블로깅을 많이 하다보면 조금씩 더 간결해지지 않을까 싶다.


블로그 이미지

B급 감성

,