SuperAPI AI Directory

math-hwpx

General & other

View repository

Updated 10-15 20:10

About this skill

math-hwpx — Skill for generating math equation problem sheets in HWPX

Math equations (hp:equation) including 2-column problem sheetSkill for generating as HWPX files. Writes math problems in the range of middle school 1st year to high school 3rd year using Hancom Office equation editor script syntax. Follows the same XML-first workflow as the hwpx skill, and is compatible with the existing hwpx skill's build/validation tools.

Supports two formats:

  • exam (default, academic achievement assessment/CSAT): National United Academic Achievement Assessment/CSAT format exam paper (header, horizontal answer choices, point values, page numbers, etc.)
  • worksheet (--exam-type worksheet must be specified): Simple 2-column math problem sheet

Environment

# SKILL_DIR는 이 SKILL.md가 위치한 디렉토리의 절대 경로로 설정
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"   # 스크립트 내에서

# hwpx 기본 스킬 경로 (검증/추출 도구 사용 시)
HWPX_SKILL_DIR="<hwpx 스킬 설치 경로>"
Python virtual environment (configure according to project)

VENV="<project>/.venv/bin/activate"


모든 Python 실행 시:
```bash
# 프로젝트의 .venv를 활성화 (pip install lxml 필요)
source "$VENV"

Directory structure

.claude/skills/math-hwpx/
├── SKILL.md                              # 이 파일
├── scripts/
│   ├── build_math_hwpx.py                # CLI + build 오케스트레이션 (~170줄)
│   ├── xml_primitives.py                 # IDGen, STYLE 상수, 기본 문단/수식 생성기
│   ├── exam_helpers.py                   # 시험지 전용 XML 생성기 (배점, 선택지, 이미지)
│   ├── table_layout.py                   # 투명 테이블 2×2 레이아웃 로직
│   ├── section_generators.py             # worksheet/exam section0.xml 조립
│   ├── hwpx_utils.py                     # 검증/패키징/메타데이터
│   ├── graph_generator.py                # 그래프 PNG 생성 (matplotlib)
│   └── test_refactor.py                  # 리그레션 테스트 스크립트
├── templates/
│   ├── base/                             # 2단 레이아웃 기본 템플릿
│   │   ├── mimetype, META-INF/*, version.xml, settings.xml, Preview/*
│   │   └── Contents/ (header.xml, section0.xml, content.hpf)
│   └── worksheet/                        # (확장용) 오버레이 템플릿
├── examples/
│   ├── sample_middle_school.json          # 중학교 문제 예시
│   ├── sample_high_school.json            # 고등학교 문제 예시
│   ├── sample_exam_2020_march.json        # 학력평가 형식 예시
│   ├── 01_middle_school_worksheet.sh      # 빌드 예제
│   ├── 02_high_school_worksheet.sh        # 빌드 예제
│   └── 03_exam_paper.sh                   # 학력평가 시험지 빌드 예제
└── references/                           # 리그레션 테스트 레퍼런스 XML

Module dependency structure

build_math_hwpx.py (CLI + build 오케스트레이션)
  ├── hwpx_utils.py (validate_xml, pack_hwpx, validate_hwpx, update_metadata, _add_images_to_manifest)
  ├── section_generators.py (generate_worksheet_section_xml, generate_exam_section_xml)
  │     ├── table_layout.py (_make_problem_cell_content, make_problem_table)
  │     │     ├── xml_primitives.py (IDGen, STYLE, make_*_para, _make_equation_run)
  │     │     └── exam_helpers.py (make_exam_problem_para, make_picture_para)
  │     └── xml_primitives.py
  └── graph_generator.py (변경 없음)

Dependency direction: primitives → helpers → table → section → build (no cycles)


Core workflow: JSON → HWPX problem sheet

1. Write problem JSON

Must follow the format below. exam_type, year, month, grade, points, choices etc. academic achievement assessment fields must be included.

{
  "exam_type": "학력평가",
  "year": 2025,
  "month": 3,
  "grade": "중2",
  "session": 2,
  "subject_area": "수학",
  "total_pages": 4,
  "question_type_label": "5지선다형",
  "problems": [
    {
      "text": "의 값은?",
      "equation": "2x + 3 = 7",
      "points": 4,
      "choices": ["1", "2", "3", "4", "5"]
    },
    {
      "text": "을 간단히 한 것은?",
      "equation": "sqrt 12 + sqrt 27",
      "points": 5,
      "choices": ["$3 sqrt 3$", "$4 sqrt 3$", "$5 sqrt 3$", "$6 sqrt 3$", "$7 sqrt 3$"]
    },
    {
      "text": "다음 중 옳은 것은?",
      "points": 4,
      "choices": ["$sqrt 4 = 2$", "$sqrt 9 = +- 3$", "$(-2)^2 = -4$", "$sqrt {16} = 4$", "$sqrt 25 = -5$"]
    },
    {
      "section_label": "주관식",
      "text": "을 인수분해하시오.",
      "equation": "x^2 - 5x + 6",
      "points": 5
    }
  ]
}

2. Build

source "$VENV"

# 학력평가 형식 (기본값 — exam_type 지정 불필요)
python3 "$SKILL_DIR/scripts/build_math_hwpx.py" \
    --problems problems.json \
    --creator "수학교사" \
    --output exam.hwpx

# 단순 worksheet 형식 (명시적 지정 필요)
python3 "$SKILL_DIR/scripts/build_math_hwpx.py" \
    --problems problems.json \
    --exam-type worksheet \
    --title "중2 일차방정식" \
    --output worksheet.hwpx

3. Validation (use hwpx skill's validate.py)

python3 "$HWPX_SKILL_DIR/scripts/validate.py" worksheet.hwpx

Problem JSON format (academic achievement assessment — default)

Important: All problem JSON must be written in academic achievement assessment format. Do not use title/subtitle/info.

{
  "exam_type": "학력평가",
  "year": 2025,
  "month": 3,
  "grade": "중3",
  "session": 2,
  "subject_area": "수학",
  "total_pages": 4,
  "question_type_label": "5지선다형",
  "problems": [
    {
      "text": "문제 본문 (수식 앞뒤 텍스트)",
      "equation": "한컴 수식 스크립트",
      "points": 4,
      "choices": ["선택지1", "$수식선택지$", "선택지3", "선택지4", "선택지5"]
    },
    {
      "text": "소문제가 있는 경우",
      "equation": "메인 수식",
      "points": 5,
      "sub_problems": [
        {"equation": "소문제 수식"}
      ],
      "choices": ["1", "2", "3", "4", "5"]
    },
    {
      "section_label": "주관식",
      "text": "을 인수분해하시오.",
      "equation": "x^2 - 5x + 6",
      "points": 5
    }
  ]
}

Field descriptions

Field Required Description
exam_type O Must be "학력평가" specified
year O School year (e.g., 2025)
month O Administration month (e.g., 3, 6, 9, 11)
grade O Grade (e.g., "Middle 1", "Middle 2", "Middle 3", "High 1", "High 2", "High 3")
session X Period (default: 2)
subject_area X Subject area (default: "Mathematics")
total_pages X Total pages (default: automatic according to number of problems)
question_type_label X Question type label (default: "Multiple-choice 5 options")
problems O Problem array
problems[].text X Problem text
problems[].equation X Independent equation (display mode)
problems[].points O Point value (integer, [N점] displayed in the form)
problems[].choices X Multiple choice with 5 options ($...$wrapped in this becomes an equation)
problems[].sub_problems X Sub-problem array [{text, equation}]
problems[].section_label X Section division label (e.g., "Short answer")

Hancom equation script syntax (hp:equation)

Basic rules

Rule Description
{ } Grouping (multiple terms into one)
~ Space (1em)
` 1/4 space
# Line break (within equation)
& Column alignment (matrices, systems of equations)
"..." Text mode (equation parsing disabled)

Fractions and roots

Equation Script Example
Fraction a over b {x+1} over {x-1}
Square root sqrt {x} sqrt {b^2 - 4ac}
nth root root n of {x} root 3 of {27}

Superscripts and subscripts

Equation Script
Superscript x^2 or x SUP 2
Subscript x_i or x SUB i
둘 다 x_i ^2

Integral·Sum·Product

Equation Script
Definite integral int _{a} ^{b} f(x) dx
Double integral dint f(x,y) dxdy
Triple integral tint f dxdydz
Series (sigma) sum _{k=1} ^{n} a_k
Product (pi) prod _{i=1} ^{n} x_i

Limit

Equation Script
Limit lim _{x -> 0} f(x)
Uppercase Lim _{n -> inf}

Parentheses

Equation Script
Auto-sized parentheses left ( {a over b} right )
Auto-sized brackets left [ x right ]
Auto-sized braces left lbrace x right rbrace
Absolute value `left

Matrix

Equation Script
Basic matrix matrix {a & b # c & d}
Parenthesis matrix pmatrix {a & b # c & d}
Bracket matrix bmatrix {1 & 0 # 0 & 1}
Determinant dmatrix {a & b # c & d}

System of equations·Conditions

Equation Script
System of equations cases {2x+y=5 # 3x-2y=4}
Aligned equations eqalign {a &= b # c &= d}
Vertical stack pile {a # b # c}

Decoration (above·below)

Decoration Script
Hat (^) hat a
Tilde tilde a
Vector arrow vec v
Overline bar x
Underline under x
점 1개 dot a
점 2개 ddot a

Greek letters

Lowercase: alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota, kappa, lambda, mu, nu, xi, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega

Uppercase: ALPHA, BETA, GAMMA, DELTA 등

Variants: vartheta, varphi, varepsilon

Special symbols

Symbol Script
Infinity inf
Partial derivative partial
Nabla nabla
Therefore therefore
Because because
For all forall
Exists exist
± +- or pm
≠ ne
≤ le or leq
≥ ge or geq
≈ approx
≡ equiv
⊂ subset
∈ in
→ -> or rarrow
← larrow
↔ <-> or lrarrow
··· cdots

Font style

Style Command
Roman (upright) rm
Italic it
Bold bold
Bold roman rmbold

Built-in functions (automatic roman)

sin, cos, tan, cot, sec, csc, arcsin, arccos, arctan, log, ln, lg, exp, det, mod, gcd, max, min, sinh, cosh, tanh


Equation XML structure

In section0.xml, equations are inserted as follows:

<hp:p id="고유ID" paraPrIDRef="22" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="9">
    <hp:equation id="고유ID" type="0" textColor="#000000"
                 baseUnit="1000" letterSpacing="0" lineThickness="100">
      <hp:sz width="0" height="0" widthRelTo="ABS" heightRelTo="ABS"/>
      <hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0"
              allowOverlap="0" holdAnchorAndSO="0" rgroupWithPrevCtrl="0"
              vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT"
              vertOffset="0" horzOffset="0"/>
      <hp:script>x = {-b +- sqrt {b^2 - 4ac}} over {2a}</hp:script>
    </hp:equation>
  </hp:run>
</hp:p>

Equation properties

Property 값 Description
baseUnit 1000 Default 10pt (100 HWPUNIT = 1pt)
textColor #000000 Equation color
lineThickness 100 Fraction line/root line thickness
treatAsChar 1 Inline equation (same line as text)

Mixed text + equation

Place text and equations together in one paragraph:

<hp:p id="..." paraPrIDRef="21" ...>
  <hp:run charPrIDRef="9"><hp:t>방정식 </hp:t></hp:run>
  <hp:run charPrIDRef="9">
    <hp:equation ...>
      <hp:script>2x + 3 = 7</hp:script>
    </hp:equation>
  </hp:run>
  <hp:run charPrIDRef="9"><hp:t> 의 해를 구하라.</hp:t></hp:run>
</hp:p>

Two-column layout settings

Of the first paragraph in section0.xml, hp:colPrset to:

<hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="2" sameSz="1" sameGap="2268"/>
Property 값 Description
type NEWSPAPER Fill in left→right order
colCount 2 2단
sameSz 1 Equal width
sameGap 2268 Column spacing 8mm

Page settings (optimized for problem sheets)

<hp:pagePr landscape="WIDELY" width="59528" height="84186" gutterType="LEFT_ONLY">
  <hp:margin header="4252" footer="4252" gutter="0"
             left="5668" right="5668" top="4252" bottom="4252"/>
</hp:pagePr>
  • Left/right margins: 20mm (narrower than the standard 30mm → expanded content area)
  • Top/bottom margin: 15mm
  • Body width: 48192 HWPUNIT (170mm)
  • Column width: (48192 - 2268) / 2 = 22962 HWPUNIT (approx. 81mm)

Style ID Map

charPr (character style)

ID Description Size Weight Italic Notes
0 Default body 10pt Regular - Hamchorom Batang
1 Default Dotum 10pt Regular - Hamchorom Dotum
2~6 Skeleton compatible Various Regular -
7 Question paper title 16pt Bold - worksheet용
8 Question number 11pt Bold - worksheet용
9 Question body 10pt Regular - Common
10 Unit name/subheading 12pt Bold - Common
11 Choices/view 9pt Regular - Common
12 Exam title line 10pt Regular - exam용
13 Subject area 18pt Bold - "Mathematics Area" for exam
14 Session label 10pt Regular - "2nd Session" for exam
15 Exam question number 10pt Bold Italic exam용
16 Points 9pt Regular - exam용 "[2점]"
17 Page number 9pt Regular - exam용

paraPr (paragraph style)

ID Alignment Line spacing Purpose
0 JUSTIFY 160% Default body
1~19 Various Various Skeleton compatible
20 CENTER 160% Title (worksheet)
21 LEFT 150% Question body (Common)
22 LEFT 140% Formula display (Common)
23 LEFT 140% Choices/view vertical (Common)
24 CENTER 130% Exam title line (exam)
25 LEFT 130% Session + subject line (exam)
26 CENTER 150% Subject area main heading (exam)
27 LEFT 140% Choices horizontal (exam, tabPr=3)
28 LEFT 140% Item type label (exam, border)
29 CENTER 130% Page bottom number (exam)

tabPr (tab settings)

ID Description
0 Default (no tabs)
1 Left auto tab
2 Right auto tab
3 5 tab stops for horizontal choices (4590, 9180, 13770, 18360)

borderFill (border)

ID Description
1 None (page border)
2 None + transparent background
3 SOLID 4 sides (for tables)
4 SOLID + #E8E8E8 background (header cell)
5 Bottom DASH line (question separator)
6 SOLID border (session/item type label)
7 Thick SOLID border (page number box)

Formula examples by grade level

Middle school (middle 1 to middle 3)

# 일차방정식
2x + 3 = 7

# 분수 방정식
{2x+1} over 3 = {x-2} over 5

# 연립방정식
cases {2x + y = 5 # 3x - 2y = 4}

# 제곱근
sqrt 12 + sqrt 27 - sqrt 48

# 부등식
3x - 5 > 2x + 1

# 이차방정식
x^2 - 5x + 6 = 0

# 피타고라스
a^2 + b^2 = c^2

# 일차함수
y = ax + b

High School Mathematics I (Grade 10)

# 지수법칙
a^m times a^n = a^{m+n}

# 로그
log _a xy = log _a x + log _a y

# 절댓값
left | x - 3 right | < 5

# 이차함수 꼭짓점
y = a(x - p)^2 + q

# 근의 공식
x = {-b +- sqrt {b^2 - 4ac}} over {2a}

High School Mathematics II (Grade 11)

# 극한
lim _{x -> 0} {sin x} over x = 1

# 미분 정의
f'(x) = lim _{h -> 0} {f(x+h) - f(x)} over h

# 정적분
int _{0} ^{pi} sin x dx = 2

# 급수
sum _{k=1} ^{n} k = {n(n+1)} over 2

# 등차수열
a_n = a_1 + (n-1)d

High School Probability and Statistics

# 조합
{_n}C{_r} = {n!} over {r!(n-r)!}

# 이항정리
(a+b)^n = sum _{k=0} ^{n} {_n}C{_k} a^{n-k} b^k

# 확률
P(A cup B) = P(A) + P(B) - P(A cap B)

# 정규분포
f(x) = {1} over {sigma sqrt {2 pi}} e^{-{(x- mu)^2} over {2 sigma ^2}}

High School Calculus

# 도함수
{d} over {dx} x^n = n x^{n-1}

# 합성함수 미분
{dy} over {dx} = {dy} over {du} times {du} over {dx}

# 부분적분
int u dv = uv - int v du

# 치환적분
int f(g(x)) g'(x) dx = int f(u) du

# 테일러 급수
e^x = sum _{n=0} ^{inf} {x^n} over {n!}

High School Geometry

# 벡터 내적
vec a cdot vec b = left | vec a right | left | vec b right | cos theta

# 원의 방정식
(x-a)^2 + (y-b)^2 = r^2

# 타원
{x^2} over {a^2} + {y^2} over {b^2} = 1

# 쌍곡선
{x^2} over {a^2} - {y^2} over {b^2} = 1

# 행렬 곱
pmatrix {a & b # c & d} pmatrix {x # y} = pmatrix {ax+by # cx+dy}

Geometry Shapes Type (Geometry Shapes)

graph_generator.pysupports 5 geometry shape types in addition to function graphs (polynomial, trig, etc.). In the problem JSON, graph If you specify the spec below in the field, a shape PNG is automatically generated.

triangle — triangle

Defined by 3 vertex coordinates. Supports interior angle display, angle arcs, side length labels, auxiliary lines (median/altitude/angle bisector), circumcircle/incircle.

{
  "type": "triangle",
  "vertices": [[0, 0], [6, 0], [2, 5]],
  "labels": {"A": [2, 5], "B": [0, 0], "C": [6, 0]},
  "show_angles": [true, true, true],
  "angle_labels": ["80°", "50°", "50°"],
  "side_labels": {"AB": "5", "BC": "6", "AC": "5"},
  "equal_marks": {"AB": 1, "AC": 1},
  "show_circumcircle": false,
  "show_incircle": false,
  "auxiliary_lines": [{"type": "bisector", "vertex": "B"}]
}
Field Required Description
vertices O 3 vertex coordinates [[x,y], [x,y], [x,y]]
labels X Vertex name→coordinate mapping
show_angles X Whether to display an angle arc for each vertex [bool, bool, bool]
angle_labels X Angle label (e.g., ["80°", "50°", "50°"])
side_labels X Side name→length label (e.g., {"AB": "5"})
equal_marks X Side name→number of equal-division marks (e.g., {"AB": 1, "AC": 1})
show_circumcircle X Display circumcircle
show_incircle X Display incircle
auxiliary_lines X Auxiliary line array. type: "median", "altitude", "bisector"

circle — circle/sector/arc

Center, radius, points on the circle, chord, tangent, arc highlight, central angle/inscribed angle display.

{
  "type": "circle",
  "center": [0, 0],
  "radius": 3,
  "show_center": true,
  "points_on_circle": [
    {"angle_deg": 30, "label": "A"},
    {"angle_deg": 150, "label": "B"}
  ],
  "chords": [["A", "B"]],
  "tangent_at": ["A"],
  "arc_highlight": {"from": "A", "to": "B", "color": "gray"},
  "central_angle": true,
  "inscribed_angle": {"vertex": "C", "arc": ["A", "B"]}
}
Field Required Description
center X Center coordinates (default: [0,0])
radius X Radius (default: 3)
show_center X Display center point O
points_on_circle X Array of points on the circle {angle_deg, label}
chords X 현 [["A","B"]]
tangent_at X Name of point where tangent is drawn
arc_highlight X Arc highlight {from, to, color}
central_angle X Display central angle segment
inscribed_angle X Inscribed angle {vertex, arc: [A, B]}

quadrilateral — quadrilateral

Defined by 4 vertex coordinates. Supports diagonals, parallel marks, equal-division marks, right angle marks.

{
  "type": "quadrilateral",
  "kind": "parallelogram",
  "vertices": [[0, 0], [5, 0], [7, 3], [2, 3]],
  "labels": {"A": [0, 0], "B": [5, 0], "C": [7, 3], "D": [2, 3]},
  "show_diagonals": true,
  "diagonal_intersection_label": "O",
  "parallel_marks": {"AB_DC": 1, "AD_BC": 2},
  "equal_marks": {"AB": 1, "DC": 1},
  "show_right_angles": [],
  "side_labels": {"AB": "10", "BC": "6"}
}
Field Required Description
vertices O 4 vertex coordinates
kind X Reference type name (parallelogram, rectangle, etc.)
labels X Vertex name→coordinate
show_diagonals X Display diagonals as dashed lines
diagonal_intersection_label X Diagonal intersection label
parallel_marks X Parallel mark {"AB_DC": 1} (AB∥DC, 1 arrow)
equal_marks X Equal-division marks
show_right_angles X Array of vertex names to mark as right angles
side_labels X Side length labels

coordinate — shapes on coordinates

Combination of coordinate axes + segments/polygons/points/lines. Linear functions and areas of shapes, etc.

{
  "type": "coordinate",
  "xlim": [-1, 7],
  "ylim": [-1, 7],
  "segments": [[[0, 6], [2, 0]], [[2, 0], [6, 0]], [[0, 6], [6, 0]]],
  "points": [
    {"pos": [0, 6], "label": "(0, 6)"},
    {"pos": [2, 0], "label": "(2, 0)"}
  ],
  "fill_polygon": [[0, 6], [2, 0], [6, 0]],
  "shade_alpha": 0.15,
  "lines": [{"slope": -3, "intercept": 6, "style": "k-"}],
  "circles": [{"center": [3, 3], "radius": 2}]
}
Field Required Description
xlim, ylim X Coordinate axis range
segments X Segment array [[[x1,y1],[x2,y2]], ...]
points X Point array {pos, label}
fill_polygon X Vertex coordinates of polygon to fill
shade_alpha X Fill opacity (default: 0.15)
lines X Line {slope, intercept, style}
circles X 원 {center, radius}

solid3d — solid figure (2D projection)

Renders exam-paper-style solid figures with 2D oblique projection. Represents back edges with dashed lines.

{
  "type": "solid3d",
  "kind": "cylinder",
  "params": {"radius": 2, "height": 4},
  "labels": {"r": "2", "h": "4"},
  "show_hidden": true
}

Supported kind: cylinder, cone, sphere, rectangular_prism, triangular_prism, pyramid

Field Required Description
kind X Solid type (default: "cylinder")
params X Parameters by type (radius, height, width, depth, base, etc.)
labels X Dimension labels (e.g., {"r": "2", "h": "4"})
show_hidden X Show back edge as dashed line (default: true)

kind별 params:

  • cylinder: {radius, height}
  • cone: {radius, height}
  • sphere: {radius}
  • rectangular_prism: {width, height, depth}
  • triangular_prism: {base, height, depth}
  • pyramid: {base, height, depth}

Writing section0.xml directly (advanced)

Write section0.xml directly instead of JSON for finer control:

SECTION=$(mktemp /tmp/section0_XXXX.xml)
cat > "$SECTION" << 'XMLEOF'
<?xml version='1.0' encoding='UTF-8'?>
<hs:sec xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph"
        xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section"
        xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core">
  <!-- base/section0.xml의 첫 문단(secPr+colPr) 그대로 복사 -->
  <!-- ... -->

  <!-- 제목 -->
  <hp:p id="1000000002" paraPrIDRef="20" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
    <hp:run charPrIDRef="7"><hp:t>수학 문제지</hp:t></hp:run>
  </hp:p>

  <!-- 수식 문단 -->
  <hp:p id="1000000003" paraPrIDRef="22" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
    <hp:run charPrIDRef="9">
      <hp:equation id="1000000099" type="0" textColor="#000000"
                   baseUnit="1000" letterSpacing="0" lineThickness="100">
        <hp:sz width="0" height="0" widthRelTo="ABS" heightRelTo="ABS"/>
        <hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0"
                allowOverlap="0" holdAnchorAndSO="0" rgroupWithPrevCtrl="0"
                vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT"
                vertOffset="0" horzOffset="0"/>
        <hp:script>x = {-b +- sqrt {b^2 - 4ac}} over {2a}</hp:script>
      </hp:equation>
    </hp:run>
  </hp:p>
</hs:sec>
XMLEOF

python3 "$SKILL_DIR/scripts/build_math_hwpx.py" --section "$SECTION" --output result.hwpx
rm -f "$SECTION"

Integration with the hwpx skill

math-hwpx can reuse the tools of the hwpx skill:

Tool Path Purpose
validate.py $HWPX_SKILL_DIR/scripts/validate.py Validate HWPX structure
unpack.py $HWPX_SKILL_DIR/scripts/office/unpack.py HWPX → directory
pack.py $HWPX_SKILL_DIR/scripts/office/pack.py directory → HWPX
text_extract.py $HWPX_SKILL_DIR/scripts/text_extract.py Extract text
# 생성된 문제지 구조 확인
python3 "$HWPX_SKILL_DIR/scripts/office/unpack.py" worksheet.hwpx ./unpacked/
# → ./unpacked/Contents/section0.xml 편집 후
python3 "$HWPX_SKILL_DIR/scripts/office/pack.py" ./unpacked/ edited.hwpx

Unit conversion (same as the hwpx skill)

값 HWPUNIT Meaning
1pt 100 Default unit
10pt 1000 Default font size
1mm 283.5 Millimeter
A4 폭 59528 210mm
A4 height 84186 297mm
Question paper left/right margin 5668 20mm
Question paper body width 48192 170mm
Column spacing 2268 8mm
Column width 22962 약 81mm

Critical Rules

  1. Always the academic assessment format: JSON must include "exam_type": "학력평가". The worksheet format must not be used
  2. Points required: Every question requires the "points" field. Multiple choice requires "choices" 5 items, short answer requires "section_label": "주관식" included
  3. Formula scripts go <hp:script> inside: Use Hancome formula syntax, not LaTeX
  4. secPr required: secPr must be included in the first paragraph of section0.xml
  5. mimetype order: When packaging as ZIP, mimetype must be the first entry, ZIP_STORED
  6. ID uniqueness: Paragraph IDs and formula IDs must all be unique within the document
  7. charPrIDRef consistency: charPr IDs referenced in section0.xml must exist in header.xml
  8. Use venv: The project's .venv/bin/activate (lxml package required)
  9. Validation required: Check integrity with validate.py after generation
  10. Formula size: baseUnit="1000" = 10pt (same as body text), adjust to 1200(12pt) etc. if needed
  11. Choice formulas: In JSON, wrapping with $...$treats it as a formula. Must use Hancome formula script syntax ($3 sqrt 2$ ✓, $3\sqrt{2}$ ✗ LaTeX not allowed). Example: $30 pi$, $sqrt 3$, ${x^2} over 2$
  12. hp:sz width/height 0: Can be set to 0 since Hancome Office calculates it automatically when rendering

Academic assessment/CSAT exam paper format (default)

The academic assessment format is the default. Every question JSON must include "exam_type": "학력평가", and each question must specify "points" points. Multiple choice includes 5 "choices", and short answer specifies "section_label": "주관식".

Exam paper JSON format

{
  "exam_type": "학력평가",
  "year": 2020,
  "month": 3,
  "grade": "고1",
  "session": 2,
  "subject_area": "수학",
  "total_pages": 12,
  "question_type_label": "5지선다형",
  "problems": [
    {
      "text": "의 값은?",
      "equation": "-{7} over {2} times (-3) + 4 times left | -{5} over {2} right |",
      "points": 2,
      "choices": ["-1", "$-{1} over {2}$", "0", "${1} over {2}$", "1"]
    },
    {
      "section_label": "주관식",
      "text": "의 값을 구하시오.",
      "equation": "2x + 3 = 7",
      "points": 4
    }
  ]
}

Exam paper-specific JSON fields

Field Required Description
exam_type O "학력평가", "수능", "exam" one of
year X School year (e.g., 2020)
month X Exam month (e.g., 3)
grade X Grade (e.g., "High 1")
session X Period (default: 2)
subject_area X Subject area (default: "Mathematics")
total_pages X Total number of pages (default: 12)
question_type_label X Question type label (default: "Multiple choice with 5 options")
problems[].points X Points (integer, [2점] displayed in the form)
problems[].section_label X Section division label (e.g., "Subjective")

Exam paper layout structure

┌─────────────────────────────────────────┐
│  2020학년도 3월 고1 전국연합학력평가 문제지   │ ← 1단 (전체너비)
│                                          │
│  제 2 교시        수학 영역               │ ← 1단 (전체너비)
├────────────────────┬────────────────────┤
│ ┌──────────┐       │                    │
│ │ 5지선다형  │       │                    │ ← 2단 시작
│ └──────────┘       │                    │
│                    │                    │
│ 1. 문제... [2점]   │ 3. 문제... [2점]   │
│  ① a ② b ③ c ④ d ⑤ e│  ① a ② b ③ c ④ d│ ← 가로 선택지
│                    │                    │
│ 2. 문제... [2점]   │ 4. 문제... [3점]   │
│  ① a ② b ③ c ④ d ⑤ e│  ① a ② b ③ c ④ d│
├────────────────────┴────────────────────┤
│                1 / 12                    │ ← 1단 (전체너비)
└─────────────────────────────────────────┘

Column switching technique

In the exam format, hp:colPris used to switch the number of columns within a section:

  1. In the secPr paragraph, start with colCount="1"(full width of the header area)
  2. After outputting the header paragraphs, switch to hp:colPr colCount="2"(2 columns for the body)
  3. Output the body questions
  4. (Optional) switch to hp:colPr colCount="1"(bottom page number)

Horizontal answer choices

Use the 5 tab stops defined in tabPr ID 3 to place the answer choices on one line:

① -1    ② -1/2    ③ 0    ④ 1/2    ⑤ 1

Between each answer choice, insert <hp:tab/>to arrange them at equal intervals.

Build example

# 학력평가 형식 (기본값)
python3 "$SKILL_DIR/scripts/build_math_hwpx.py" \
    --problems problems.json \
    --creator "교육청" \
    --output exam.hwpx