[정보] IT정보&활용 2024. 10. 23. 18:56

[구글 Apps Script] PDF로 출력하기

반응형

[구글 스프레드 시트]로 일지를 만든 경우, 이 일지의 날짜를 하나하나 변경하면서 PDF로 출력해 저장해주는 스크립트 입니다.

이 모두를 묶어서 하나의 PDF로 구현하는 것을 고민하였으나, 아직까지 구글에서는 PDF 합치기 기능은 제공하고 있지 않는거 같습니다.

 

메뉴바에 출력 버튼이 만들어지고, 적용 월을 입력하면, 그 달에 해당하는 일지를 모두 PDF로 만들어줍니다.

단, 한꺼번에 많은 양을 PDF로 변환하면 구글에서 오류 메시지를 나타내었습니다.

해서 10개씩 출력한 후 30초 기다렸다가 다시 출력하도록 했습니다.

그래서 모두 PDF로 변환되는데 3분 안쪽의 시간이 필요합니다.

// 이후의 각주를 참고하셔서 자신의 시트에 맞게 수정해서 사용하시면 됩니다.

또한 PDF가 저장될 폴더에 접근 권한을 열어 두는거 잊지마세요~

 

----------------------------------

Code.gs

----------------------------------

function onOpen() {

  var ui = SpreadsheetApp.getUi();

  ui.createMenu('출력')

    .addItem('월 입력', 'generatePDFsByMonth')

    .addToUi();

}

 

function generatePDFsByMonth() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();

  var sheet1Name = 'DATA';

  var sheet2Name = '일지'; // 출력서식이 있는 시트명을 정확히 지정해주세요.

  var dateCell = 'A3'; // '일지' 시트에서 날짜가 입력되는 셀입니다.

  var outputRange = 'A1:G25'; // '일지' 시트의 출력범위를 조정합니다.

  var dataRange = 'A2:A'; // 'DATA'시트에서의 날짜 데이터 범위

  var folderId = '1OlXGiKJyeixZ0j4PLIkWl1xxffBZVQZ0'; // PDF가 저장될 폴더의 ID입니다.

 

  var sheet1 = ss.getSheetByName(sheet1Name);

  var sheet2 = ss.getSheetByName(sheet2Name);

  var folder = DriveApp.getFolderById(folderId);

  var ui = SpreadsheetApp.getUi();

  var response = ui.prompt('월을 입력하세요 (YYYY-MM)', ui.ButtonSet.OK_CANCEL);

 

  if (response.getSelectedButton() == ui.Button.OK) {

    var month = response.getResponseText().trim();

    var data = sheet1.getRange(dataRange + sheet1.getLastRow()).getValues();

    var filteredData = data.filter(function(row) {

      return row[0] && formatDate(row[0]).includes(month);

    });

 

    for (var i = 0; i < filteredData.length; i += 10) {

      var chunk = filteredData.slice(i, i + 10);

      chunk.forEach(function(row) {

        sheet2.getRange(dateCell).setValue(formatDate(row[0]));

        SpreadsheetApp.flush();

        Utilities.sleep(1000); // 데이터를 로드하는 데 충분한 시간을 두기 위해 1초 대기

        var pdfBlob = createPDF(sheet2, formatDate(row[0]) + '_' + sheet2Name + '.pdf', outputRange, folder);

        folder.createFile(pdfBlob);

      });

      Utilities.sleep(30000); // 10개의 파일 생성 후 30초 대기

    }

 

    if (filteredData.length > 0) {

      ui.alert('PDF 저장이 완료되었습니다: ' + month);

    } else {

      ui.alert('입력된 월에 해당하는 데이터가 없습니다: ' + month);

    }

  }

}

 

function formatDate(date) {

  var d = new Date(date);

  var year = d.getFullYear();

  var month = ('0' + (d.getMonth() + 1)).slice(-2);

  var day = ('0' + d.getDate()).slice(-2);

  return year + '-' + month + '-' + day;

}

 

function createPDF(sheet, fileName, range, folder) {

  var sheetName = sheet.getName();

  var url = 'https://docs.google.com/spreadsheets/d/' + SpreadsheetApp.getActiveSpreadsheet().getId() +

            '/export?exportFormat=pdf&format=pdf' +

            '&size=A4' +

            '&portrait=true' +

            '&fitw=true' +

            '&sheetnames=false&printtitle=false' +

            '&pagenumbers=false' +

            '&gridlines=false' +

            '&fzr=false' +

            '&gid=' + sheet.getSheetId() +

            '&range=' + range;

 

  var token = ScriptApp.getOAuthToken();

  var response = UrlFetchApp.fetch(url, {

    headers: {

      'Authorization': 'Bearer ' + token

    }

  });

 

  var pdfBlob = response.getBlob().setName(fileName);

  return pdfBlob;

}



반응형

구글 Apps Script로 후원신청서 만들기

반응형

구글 Apps Script로 입력폼을 만들고, 입력된 데이터들이 구글 스프레드 시트에 저장되도록 하는 스크립트를 구현해 보았습니다.

 

Apps Script는 두 개의 파일로 작동합니다.

Code.gs라는 이름의 스크립트 파일과 웹화면을 통해 보여질 index.html이 그것입니다.

 

※ 이 스크립트는 기본적으로 구글 스프레드 시트의 Apps Script를 다룰 줄 아는 사람을 대상으로 하기 때문에 스크립트 적용 이전단계에 대해서는 설명하지 않습니다.

 

Code.gs

 
// 스프레드시트 ID와 시트 이름, 서명이미지 저장 폴더 ID를 변수로 선언
var SPREADSHEET_ID = ' YOUR_SPREADSHEET_ID';
var SHEET_NAME = '시트1';
var FOLDER_ID = ' YOUR_FOLDER_ID';

function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}

function sendUserData(name, gender, ssn_a, ssn_b, cphone_n, phone_n, address, e_mail, s_term, s_type, s_money, s_how, s_item, s_item_q, s_unit, s_sector, s_why, s_path, receipt1, receipt2, a_date, signature) {
  var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
  var imageUrl = saveSignatureToDrive(signature, name);
  sheet.appendRow([
    name, gender, ssn_a, ssn_b,
    "'" + cphone_n, // 앞에 작은 따옴표 추가
    "'" + phone_n,  // 앞에 작은 따옴표 추가
    address, e_mail, s_term, s_type, s_money, s_how,
    s_item, s_item_q, s_unit, s_sector, s_why, s_path,
    receipt1, receipt2, a_date, '=IMAGE("' + imageUrl + '")'
  ]);
}

function saveSignatureToDrive(signature, name) {
  var folder = DriveApp.getFolderById(FOLDER_ID);
  var blob = Utilities.newBlob(Utilities.base64Decode(signature.split(',')[1]), 'image/png', name + '_signature.png');
  var file = folder.createFile(blob);
  var fileId = file.getId();
}

 

index.html

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <title>부민노인복지관 후원신청서</title>
  <style>
    #sig {
      background-color: #e0e0e0;
      border: 1px solid black;
      width: 500px;
      height: 300px;
    }

    #s_item_q, #ssn_a {
      width: 6em;
    }

    #ssn_b {
      width: 7em;
    }

    #savingMessage {
      display: none;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: white;
      border: 1px solid black;
      padding: 20px;
      z-index: 1000;
    }

  </style>
</head>
<body>
  <p>부민노인복지관 후원신청서</p>
  <form id="donationForm">
    <label>이름</label>
    <input type="text" id="name" name="name" placeholder="이름" required><br>

    <label>성별</label>
    <input type="radio" name="gender" value="남">남자
    <input type="radio" name="gender" value="여">여자<br>

    <label>주민등록번호</label>
    <input type="text" id="ssn_a" name="ssn_a" size="6" maxlength="6" required>-
    <input type="password" id="ssn_b" name="ssn_b" size="7" maxlength="7" required><br>

    <label>휴대전화</label>
    <input type="text" id="cphone_n" name="cphone_n" size="11" maxlength="11"> ※ 숫자만 입력<br>

    <label>일반전화</label>
    <input type="text" id="phone_n" name="phone_n" size="11" maxlength="11"> ※ 숫자만 입력<br>

    <label>주소</label>
    <input type="text" id="address" name="address" size="30"><br>

    <label>이메일</label>
    <input type="text" id="e_mail" name="e_mail" size="30"><br>

    <label>후원방식</label>
    <input type="radio" name="s_term" value="정기">정기
    <input type="radio" name="s_term" value="비정기">비정기<br>

    <label>후원구분</label>
    <input type="radio" name="s_type" value="지정">지정
    <input type="radio" name="s_type" value="비지정">비지정<br>

    <label>후원금</label>
    <input type="text" id="s_money" name="s_money"><br>

    <label>후원방법</label>
    <input type="radio" name="s_how" value="자동이체">자동이체
    <input type="radio" name="s_how" value="현금납부">현금납부
    <input type="radio" name="s_how" value="CMS">CMS 이체
    <input type="radio" name="s_how" value="기타">기타<br>

    <label>후원품</label>
    (품명) <input type="text" id="s_item" name="s_item"> /
    (수량) <input type="number" id="s_item_q" name="s_item_q" size="4"> /
    (단위) <select id="s_unit" name="s_unit">
            <option value="">---</option>
            <option value="개"></option>
            <option value="세트">세트</option>
            <option value="Box">Box</option>  
            <option value="건"></option>  
            <option value="Kg">Kg</option>  
            <option value="명"></option>  
            <option value="포"></option>  
            <option value="g">g(그램)</option>  
            <option value="리터">리터</option>  
            <option value="대"></option>  
            <option value="장"></option>  
            <option value="통"></option>  
            <option value="판"></option>  
            <option value="병"></option>  
            <option value="마리">마리</option>  
            <option value="봉지">봉지</option>  
            <option value="팩"></option>  
            <option value="묶음">묶음</option>  
            <option value="그루">그루</option>  
            <option value="송이">송이</option>  
            <option value="포기">포기</option>  
            <option value="자루">자루</option>  
            <option value="인분">인분</option>  
            <option value="짝"></option>  
            <option value="포대">포대</option>  
            <option value="기타">기타</option>  
    </select><br>

    <label>후원분야</label>
    <select id="s_sector" name="s_sector">
      <option value="">---------</option>
      <option value="밑반찬지원">밑반찬지원</option>
      <option value="저소득노인 무료급식사업">저소득노인 무료급식사업</option>
      <option value="위기 및 취약노인 사업">위기 및 취약노인 사업</option>
      <option value="지역복지 연계사업">지역복지 연계사업</option>
      <option value="명절 정나눔 행사">명절 정나눔 행사</option>
      <option value="기타">기타</option>
    </select><br>

    <label>참여동기</label>
    <select id="s_why" name="s_why">
      <option value="">---------</option>
      <option value="경제적 여유가 있어서">경제적 여유가 있어서</option>
      <option value="불우한 이웃을 돕기위해">불우한 이웃을 돕기위해</option>
      <option value="지역사회복지를 위해">지역사회복지를 위해</option>
      <option value="종교적 신념으로">종교적 신념으로</option>
      <option value="기타">기타</option>
    </select><br>

    <label>참여경로</label>
    <select id="s_path" name="s_path">
      <option value="">---------</option>
      <option value="기존 후원자/자원봉사자 소개를 통해">기존 후원자/자원봉사자 소개를 통해</option>
      <option value="복지관 소식지나 안내물을 통해">복지관 소식지나 안내물을 통해</option>
      <option value="언론매체를 통해">언론매체를 통해(TV, 라디오, 신문 등)</option>
      <option value="특정 행사 및 기념일 축하">특정 행사 및 기념일 축하</option>
      <option value="기타">기타</option>
    </select><br>

    <label>후원금영수증 발급</label><br>
      <input type="checkbox" name="receipt1" value="후원금영수증 필요">후원금 영수증 필요<br>
      <input type="checkbox" name="receipt2" value="국세청 연말정산 간소화 후원내역 제공 동의">국세청 연말정산 간소화 후원내역 제공 동의 <br>

    <label>신청일자</label>
    <input type="date" id="a_date" name="a_date"><br>

    <label>서명</label><br>
    <canvas id="sig"></canvas><br>
    <button type="button" id="clearSig">서명 지우기</button><br><br>

    <button type="button" onclick="submitData()">제출</button>
  </form>

  <div id="savingMessage">저장 중입니다...</div>

  <script>
    var signaturePad;

    function setupSignatureBox() {
      var canvas = document.getElementById("sig");
      canvas.width = 500;  // 명시적으로 크기 설정
      canvas.height = 300; // 명시적으로 크기 설정
      signaturePad = new SignaturePad(canvas);
    }

    function clearSignature() {
      signaturePad.clear();
    }

    function submitData() {
      const form = document.getElementById('donationForm');
      const formData = new FormData(form);

      // 필수 입력 필드 확인
      if (!formData.get('name') || !formData.get('ssn_a') || !formData.get('ssn_b') || !formData.get('cphone_n') || signaturePad.isEmpty()) {
        alert('성명, 주민등록번호, 서명은 필수 입력 항목입니다.');
        return;
      }

      const data = {
        name: formData.get('name'),
        gender: formData.get('gender'),
        ssn_a: formData.get('ssn_a'),
        ssn_b: formData.get('ssn_b'),
        cphone_n: formData.get('cphone_n'),
        phone_n: formData.get('phone_n'),
        address: formData.get('address'),
        e_mail: formData.get('e_mail'),
        s_term: formData.get('s_term'),
        s_type: formData.get('s_type'),
        s_money: formData.get('s_money'),
        s_how: formData.get('s_how'),
        s_item: formData.get('s_item'),
        s_item_q: formData.get('s_item_q'),
        s_unit: formData.get('s_unit'),
        s_sector: formData.get('s_sector'),
        s_why: formData.get('s_why'),
        s_path: formData.get('s_path'),
        receipt1: formData.get('receipt1') ? '필요' : '불필요',
        receipt2: formData.get('receipt2') ? '동의' : '미동의',
        a_date: formData.get('a_date'),
        signature: signaturePad.toDataURL()
      };

      document.getElementById('savingMessage').style.display = 'block'; // 저장 중 메시지 표시

      google.script.run.withSuccessHandler(() => {
        document.getElementById('savingMessage').style.display = 'none'; // 저장 중 메시지 숨기기
        alert('데이터가 성공적으로 저장되었습니다.');
      }).withFailureHandler(error => {
        document.getElementById('savingMessage').style.display = 'none'; // 저장 중 메시지 숨기기
        console.error('Error:', error);
        alert('데이터 저장 중 오류가 발생했습니다. 다시 입력해주세요.');
      }).sendUserData(
        data.name, data.gender, data.ssn_a, data.ssn_b, data.cphone_n, data.phone_n,
        data.address, data.e_mail, data.s_term, data.s_type, data.s_money, data.s_how,
        data.s_item, data.s_item_q, data.s_unit, data.s_sector, data.s_why, data.s_path,
        data.receipt1, data.receipt2, data.a_date, data.signature
      );
    }

    document.getElementById("clearSig").addEventListener("click", clearSignature);
    document.addEventListener("DOMContentLoaded", setupSignatureBox);
  </script>
</body>
</html>

 

입력 폼은 아래 링크와 같이 보여지게 됩니다.

https://script.google.com/a/macros/bmsenior.org/s/AKfycbxOwy1AJqVZypRUhlJtNl3xzWDSm4mUWhKxtBwI0_FrT66QLX_6ruxFn0h0WR4JUYrA3w/exec

 

https://script.google.com/a/macros/bmsenior.org/s/AKfycbxOwy1AJqVZypRUhlJtNl3xzWDSm4mUWhKxtBwI0_FrT66QLX_6ruxFn0h0WR4JUYrA3w/exec

 

script.google.com

 

반응형

Apps Script를 활용해 스프레드 시트로 데이터 전송하기

반응형

우리가 입력하는 데이터를 구글 스프레드 시트에 저장되게 하려면 어떻게 해야할까요?

기본적으로 구글 설문지 폼을 사용하면 스프레드 시트에 저장되는 것을 확인할 수 있습니다.

이걸 내가 원하는 대로 할 수는 없을까요?

 

일반적으로 생각했을 때 2개의 서식이 필요하단 사실은 떠올릴 수 있을 것입니다.

첫번째로 데이터를 입력하는 서식이며, 이는 웹 화면을 통해 보여질 것입니다.

구글 스프레드시트의 Apps Script에서는 기본값으로 index.html을 설정해두고 있습니다.

두번째로 입력된 데이터가 스프레드 시트에 차곡차곡 쌓여야할 것입니다. 

이를 위한 스프레드 시트 파일(SpreadSheet)이 필요합니다.

 

그런데 하나가 더 필요합니다. 

이 두 파일을 연결할 연결고리, 그것이 Apps Script 입니다.

Code.gs가 바로 그것입니다.

 

자, 정리하자면, 데이터입력을 담당하는 index.html, 데이터를 받아서 저장할 SpreadSheet, 마지막으로 이 둘을 연결하는 Code.gs가 필요합니다.

 

이때 필요한 가장 기본적인 코드를 정리해보겠습니다.

 

  1. index.html

 

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">


    <!-- 아래 스크립트를 통해 구글 스프레드 시트에 저장하게 됩니다. -->
    <script>
      function onSuccess(){
        alert("저장완료");
      }


      function resister() {
        let inputEl = document.getElementsByTagName("input");
        let inputData = [];
        for(element of inputEl) {
          inputData.push(element.value);
        }


        google.script.run.withSuccessHandler(onSuccess).sendUserData(inputData);
      }
    </script>
    <!-- 여기까지 -->
  </head>


  <body>
    <p>데이터 입력</p>
    <!-- 아래 input 태그를 통해 데이터가 입력되며, resister()를 통해 등록됩니다. -->
    <label>종류</label>
    <input type="text" name="type"><br>


    <label>수량</label>
    <input type="number" name="number"><br>


    <label>유효기간</label>
    <input type="date" name="deadline"><br>


    <button onclick="resister()">등록</button>
    <!-- 여기까지. 위에 input은 더 많이 추가해도 됩니다. -->
  </body>
 
</html>



  1. Code.gs
  • index.html과 소통하기 위해 필요한 함수
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}

 

  • 아래 3개 중 필요에 따라 선택
function sendUserData(inputData) {
  let ss = SpreadsheetApp.getActiveSheet();
  ss.appendRow(inputData);
}
▲ 구글 스프레드 시트에서 바로 App Script를 만든 경우로 시트가 하나 뿐인 경우.
  별도의 지정없이 바로 작동합니다.
function sendUserData(inputData) {
  let sheetName = '시트1'; // 시트 이름을 변수로 선언
  let ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  ss.appendRow(inputData);
}
▲ 시트가 여러개인 경우 시트명을 변수로 지정토록 하였습니다.
  ’시트1’을 수정하세요.
function sendUserData(inputData) {
  // 스프레드시트 파일 ID 및 시트 이름 설정
  const spreadsheetId = 'YOUR_SPREADSHEET_ID'; // 여기에 스프레드시트 파일 ID를 입력
  const sheetName = '시트1';


  // 스프레드시트 및 시트 가져오기
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  const sheet = spreadsheet.getSheetByName(sheetName);


  // 데이터 추가
  sheet.appendRow(inputData);
}
▲ 파일명(고유ID)와 시트명을 모두 변수로 설정합니다.
  ’YOUR_SPREADSHEET_ID’에 스프레드 시트 파일의 ID를 직접 입력합니다.
  ’시트1’에 시트명을 직접 입력합니다.



여기까지가 기본적인 처리방법입니다.

index.html을 수정함으로써 입력화면을 좀더 다양하게 꾸밀 수 있으며, 스프레드 시트로 보낼 데이터도 추가할 수 있을 것입니다.



반응형
[정보] IT정보&활용 2024. 8. 27. 13:14

[구글 스프레드 시트] 서명받기

반응형

http://swsp.or.kr/3.html

 

appsscript활용

4 대상자 검색,수정,추가하기1 동영상 대상자 검색,수정,추가하기2 동영상 대상자 검색,수정,추가하기3 동영상 대상자 검색,수정,추가하기4 동영상 대상자 검색,수정,추가하기5 동영상 대상자 검

swsp.or.kr

여기에 가보면 구글 스프레드 시트 의 앱 스크립트를 활용한 다양한 사례들을 보여주고 있다.

그 중 주목한 것 하나가 바로 "서명받기"

후원신청서나 프로그램 참가 신청을 받을 때에도 유용할 거 같아서 좀더 자세히 살펴보았다.

소스는 다 오픈되어 있어 하나하나 뜯어보면서 나에게 필요한 방식으로 수정해보았다.

 

https://script.google.com/a/macros/bmsenior.org/s/AKfycbyU6cbIQfPkMm9Soqcz5l9ORI_QB320vAogSsqQFnGYoLEVxM118wXig9rsB32ogHc/exec

여기에 입력하면, 

 

https://docs.google.com/spreadsheets/d/13EJTaSd31_r8A8I4v6WzGqr1BJfqsmVmu4Xv55IpZpw/edit?gid=0#gid=0

여기에 이름과, 서명이미지, 그리고 해당 이미지의 url을 저장해 보여준다.

 

그런데 이 걸 내가 사용하기 위해서는 몇가지 수정해야할 것이이 있다.

서명이 저장되는 ① 구글 스프레드 시트의 고유 ID를 알아야하고, ② 서명 이미지가 저장될 폴더의 고유 ID를 알아야 한다.

마지막으로 ③ 저장될 시트의 이름도 일치시켜야 한다.

 

그래서 내가 편하려고 소스를 좀 수정해보았다.

 

1. Code.gs 

function doGet() {
  return HtmlService.createTemplateFromFile("index").evaluate();
}

function receiveSiganture(encodedImage,signame){

  // 폴더, 스프레드시트 ID 및 시트명을 직접 입력해주세요.
  const folderId = "23GF3aHMo0MDOxjCQLss12Ir-WjQmQT_T7B9";
  const spreadsheetId = "2Ak0WTqcdsc25RcaQZ86dRNMbddcOn88_eIdo";
  const sheetName = "시트1";

  var fileTime = new Date();
  var s1 = signame.sigName;
  const data = encodedImage.split(",")[1];
  const dataDecoded = Utilities.base64Decode(data);
  const signatureAsPictureBlob = Utilities.newBlob(dataDecoded).setName(s1+fileTime+"somefile.png");

  // 서명이 저장될 폴더
  var signurl = DriveApp.getFolderById(folderId).createFile(signatureAsPictureBlob).getId();
  var signfile = '=image("https://drive.google.com/uc?export=view&id=' + signurl + '")';
  var signfile2 = "https://drive.google.com/drive/folders/" + folderId + "/uc?export=view&id=" + signurl;
 
  // 입력된 서명 정보가 저장될 스프레드 시트
  var sa = SpreadsheetApp.openById(spreadsheetId);
  var sheet1 = sa.getSheetByName(sheetName);
  var rowData = [[fileTime, s1, signfile, signfile2]];
  var lastRow = sheet1.getLastRow();

  sheet1.getRange(lastRow+1, 1, 1, 4).setValues(rowData);
  getTexttS();
 
}

function getTexttS(form){
       var text = "서명이 입력되었습니다.";
       return text;
}

이제 위 음영처리한 ID와 이름만 정확히 확인해서 입력하면, 다른 소스는 수정할 필요가 없다.

 

2. index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>서명받기</title>
  </head>
  <body>
    <form id="signatureForm">
     
      <div id="app" class="container">
        &nbsp;<h2>성함을 입력하시고 아래 패드에 서명해 주세요.</h2><br><br>
        &nbsp;성명: <input type="text" name="mname" id="mname"><br><br>
        <div class="">
        <canvas id="sig" style="background-color: #e0e0e0; border: 1px solid black; width: 500px height: 300px;">
        </canvas>
        </div>
        <div>
          <button type="button" id="clearSig">Clear</button>
          <button type="button" id="send">Send</button>
        </div>
       
      </div>
      <input type="hidden" id="mtext" name="mtext">
    </form>
    <!-- 서명패드 스크립트: 오픈소스이며, 무료입니다. 아래는 원본 링크 -->
    <script>

      var signaturePad

      function setupSignatureBox(){
       
        var canvas = document.getElementById("sig");
        signaturePad = new SignaturePad(canvas);
      }

      function clearSignature(){
        signaturePad.clear();
      }

      function sendToDrive(){
        if(mname.value.length == 0){
          alert("성명을 입력하세요");
          mname.focus();
        } else {
        var imageData = signaturePad.toDataURL();
        var signame = {sigName: document.getElementsByName('mname')[0].value};
        google.script.run.receiveSiganture(imageData, signame);
        google.script.run.withSuccessHandler(getText).getTexttS(document.forms[0]);
        setTimeout('note()', 2000);
        clearSignature()
        // console.log(imageData);
        }
      }

      function note(){
        var alt = document.getElementById('mtext').value;
        alert(alt);
        document.getElementById("signatureForm").reset();
      }

      function getText(text){
        var txtTextt = document.getElementById('mtext');
        txtTextt.value = text;
      }

      document.getElementById("clearSig").addEventListener("click",clearSignature);
      document.getElementById("send").addEventListener("click",sendToDrive);
      document.addEventListener("DOMContentLoaded",setupSignatureBox);
    </script>

  </body>
</html>

원본에서는 1개의 css 파일과 2개의 js 파일을 참조토록 하고 있었다.

하지만 굳이 디자인이 복잡할 필요가 있는게 아니라서 과감히 삭제했다.

그리고 꼭 필요한 서명용 js 파일을 확인하고, 원본 사이트에 들어가 가장 최신의 스크립트로 교체하였다.

 

github에서 배포되고 있으며, 확인결과 상업적 사용까지 무료인 것을 확인하였다.

https://github.com/szimek/signature_pad

 

GitHub - szimek/signature_pad: HTML5 canvas based smooth signature drawing

HTML5 canvas based smooth signature drawing. Contribute to szimek/signature_pad development by creating an account on GitHub.

github.com

그리고 이를 웹에서 링크로 사용할 수 있도록 제공하는 url은 위와 같다.

 

사실 gemini에게 물어보면 굳이 링크에 있는 스크립트를 사용하지 않아도 이미지를 그릴 수 있는 소스를 만들어준다.

 

1. Code.gs

// 그리기 기능
function doGet() {
  var html = HtmlService.createHtmlOutputFromFile('index');
  return html;
}

// 지우개 기능 (선의 색을 흰색으로 설정)
function erase() {
  ctx.strokeStyle = 'white';
  // 드로잉 로직과 동일하게 사용
}

 

2. index.html

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
</head>
<body>

    성명: <input type="text" name="ur_name" id="ur_name"><br><br>
    <input type="hidden" id="n_text" name="n_text">
    <canvas id="myCanvas" style="background-color: #e0e0e0; border: 1px solid black; width: 500px height: 300px;"></canvas>
    <button id="saveButton">저장</button>
    <button id="clearButton">지우기</button>

  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    let isDrawing = false;
    let lastX = 0;
    let lastY = 0;

  function startDrawing(e) {
    isDrawing = true;
    [lastX, lastY] = [e.offsetX, e.offsetY];
  }

  function draw(e) {
    if (!isDrawing) return;
    ctx.beginPath();
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(e.offsetX, e.offsetY);
    ctx.stroke();
    [lastX, lastY] = [e.offsetX, e.offsetY];
  }

  function stopDrawing() {
    isDrawing = false;
  }

  canvas.addEventListener('mousedown', startDrawing);
  canvas.addEventListener('mousemove', draw);
  canvas.addEventListener('mouseup', stopDrawing);
  canvas.addEventListener('mouseout', stopDrawing);

  // 터치 이벤트 추가 (모바일 지원)
  canvas.addEventListener('touchstart', startDrawing);
  canvas.addEventListener('touchmove', draw);
  canvas.addEventListener('touchend', stopDrawing);

  // 지우기 버튼 클릭 이벤트 처리
  const clearButton = document.getElementById('clearButton');
 
  clearButton.addEventListener('click', () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  });

  </script>
</body>
</html>

 

처음의 드로잉처럼 자연스럽진 않지만, 그래도 웬만큼 흉내는 된다.

이걸 다시 세세한 부분을 손봐야하는데, 귀찮아서... ㅡ.ㅡ

반응형

구글 스프레드 시트로 데이터 분석하기

반응형

구글 스프레드 시트를 이용해 데이터를 분석할 수 있도록 만들어 보았습니다.

 

2024.05.27 - [[楞嚴] 생각 나누기/[平] 사회조사와 데이터분석] - Excel로 통계분석하기 #3

 

Excel로 통계분석하기 #3

Excel(엑셀)은 데이터 분석을 위해, 추가기능을 통해 분석도구를 제공하고 있다.2021.11.07 - [[정보] 복지 이야기/[福] 복지 배우기] - Excel로 통계분석하기 #1 Excel로 통계분석하기 #1굳이 비싼 통계 프

welfareact.net

이전에도 엑셀로 통계분석하는 파일을 만들기도 하였지만, 해당 파일은 결측값이 있는 경우에 대한 반영이 정확히 되어 있지 않습니다.

 

이번 버전에서는 결측값에 대한 처리를 SPSS의 결과 값과 비교하면서 보완을 해보았습니다.

 

https://docs.google.com/spreadsheets/d/1bvD_d5KbLt4yeyXHhg6FfbowdwjgjQHWTroWbvNYyag/edit?usp=sharing

 

데이터 분석

목차 구글 스프레드 시트로 데이터 분석하기 by,정수홍, jshever@hanmail.net <a href="http://welfareact.net/">https://welfareact.net</a> update 2024. 7. 27. 목차,DATA set,활용 <a href="#gid=1653135853">영가설 기각의 의미</a>,<a

docs.google.com

 

현재 모수검정 중 구현이 가능한 부분은 완료한 상태이며,

추가로 비모수검정인 순위상관분석과 카이제곱검정은 어떻게 하면 좋을지 검토 중에 있습니다.

추가로 비모수 검정인 순위상관분석과 카이제곱검정을 추가하였습니다.(2024. 9. 1)

 

혹시 오류가 있다면 알려주시면 반영해보겠습니다.

이 문서의 활용은 Data Set에 분석할 데이터를 입력하시면 됩니다.

직접 수식을 수정해보시려면 "사본 만들기"를 하셔 테스트해보세요~

반응형