|
|
|
|
|
|
|
|
from io import BytesIO |
|
|
from unittest import TestCase |
|
|
import binascii |
|
|
import codecs |
|
|
import json |
|
|
import sys |
|
|
|
|
|
from hwp5.binmodel import BinData |
|
|
from hwp5.binmodel import BorderFill |
|
|
from hwp5.binmodel import Control |
|
|
from hwp5.binmodel import ControlChar |
|
|
from hwp5.binmodel import ControlData |
|
|
from hwp5.binmodel import FaceName |
|
|
from hwp5.binmodel import GShapeObjectControl |
|
|
from hwp5.binmodel import HeaderParagraphList |
|
|
from hwp5.binmodel import Hwp5File |
|
|
from hwp5.binmodel import LanguageStruct |
|
|
from hwp5.binmodel import ListHeader |
|
|
from hwp5.binmodel import ModelStream |
|
|
from hwp5.binmodel import ParaLineSegList |
|
|
from hwp5.binmodel import ParaText |
|
|
from hwp5.binmodel import Paragraph |
|
|
from hwp5.binmodel import RecordModel |
|
|
from hwp5.binmodel import ShapeComponent |
|
|
from hwp5.binmodel import Style |
|
|
from hwp5.binmodel import TableBody |
|
|
from hwp5.binmodel import TableCaption |
|
|
from hwp5.binmodel import TableCell |
|
|
from hwp5.binmodel import TableControl |
|
|
from hwp5.binmodel import TextboxParagraphList |
|
|
from hwp5.binmodel import init_record_parsing_context |
|
|
from hwp5.binmodel import model_to_json |
|
|
from hwp5.binmodel import parse_model |
|
|
from hwp5.binmodel import parse_models |
|
|
from hwp5.binmodel import parse_models_intern |
|
|
from hwp5.dataio import Enum |
|
|
from hwp5.dataio import Flags |
|
|
from hwp5.dataio import UINT32 |
|
|
from hwp5.dataio import WORD |
|
|
from hwp5.recordstream import Record |
|
|
from hwp5.recordstream import read_records |
|
|
from hwp5.tagids import HWPTAG_BEGIN |
|
|
from hwp5.treeop import STARTEVENT, ENDEVENT |
|
|
from hwp5.treeop import prefix_event |
|
|
from hwp5.utils import cached_property |
|
|
|
|
|
from . import test_recordstream |
|
|
from .fixtures import get_fixture_path |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def TestContext(**ctx): |
|
|
''' test context ''' |
|
|
if 'version' not in ctx: |
|
|
ctx['version'] = (5, 0, 0, 0) |
|
|
return ctx |
|
|
|
|
|
|
|
|
testcontext = TestContext() |
|
|
|
|
|
|
|
|
class TestRecordParsing(TestCase): |
|
|
def test_init_record_parsing_context(self): |
|
|
record = dict(tagid=HWPTAG_BEGIN, payload=b'abcd') |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
|
|
|
self.assertEqual(record, context['record']) |
|
|
self.assertEqual(b'abcd', context['stream'].read()) |
|
|
|
|
|
|
|
|
class BinEmbeddedTest(TestCase): |
|
|
ctx = TestContext() |
|
|
stream = BytesIO(b'\x12\x04\xc0\x00\x01\x00\x02\x00\x03\x00' |
|
|
b'\x6a\x00\x70\x00\x67\x00') |
|
|
|
|
|
def testParse(self): |
|
|
record = next(read_records(self.stream)) |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
self.assertTrue(BinData, model['type']) |
|
|
self.assertEqual(BinData.StorageType.EMBEDDING, |
|
|
BinData.Flags(model['content']['flags']).storage) |
|
|
self.assertEqual(2, model['content']['bindata']['storage_id']) |
|
|
self.assertEqual('jpg', model['content']['bindata']['ext']) |
|
|
|
|
|
|
|
|
class LanguageStructTest(TestCase): |
|
|
def test_cls_dict_has_attributes(self): |
|
|
FontFace = LanguageStruct(b'FontFace', WORD) |
|
|
self.assertTrue('attributes' in FontFace.__dict__) |
|
|
|
|
|
|
|
|
class TestBase(test_recordstream.TestBase): |
|
|
|
|
|
@cached_property |
|
|
def hwp5file_bin(self): |
|
|
return Hwp5File(self.olestg) |
|
|
|
|
|
hwp5file = hwp5file_bin |
|
|
|
|
|
|
|
|
class FaceNameTest(TestBase): |
|
|
hwp5file_name = 'facename.hwp' |
|
|
|
|
|
def test_font_file_type(self): |
|
|
|
|
|
docinfo = self.hwp5file.docinfo |
|
|
facenames = (model for model in docinfo.models() |
|
|
if model['type'] is FaceName) |
|
|
facenames = list(facenames) |
|
|
|
|
|
facename = facenames[0]['content'] |
|
|
self.assertEqual(u'굴림', facename['name']) |
|
|
self.assertEqual(FaceName.FontFileType.TTF, |
|
|
facename['flags'].font_file_type) |
|
|
|
|
|
facename = facenames[3]['content'] |
|
|
self.assertEqual(u'휴먼명조', facename['name']) |
|
|
self.assertEqual(FaceName.FontFileType.HFT, |
|
|
facename['flags'].font_file_type) |
|
|
|
|
|
facename = facenames[4]['content'] |
|
|
self.assertEqual(u'한양신명조', facename['name']) |
|
|
self.assertEqual(FaceName.FontFileType.HFT, |
|
|
facename['flags'].font_file_type) |
|
|
|
|
|
|
|
|
class DocInfoTest(TestBase): |
|
|
hwp5file_name = 'facename2.hwp' |
|
|
|
|
|
def test_charshape_lang_facename(self): |
|
|
|
|
|
docinfo = self.hwp5file.docinfo |
|
|
styles = list(m for m in docinfo.models() |
|
|
if m['type'] is Style) |
|
|
|
|
|
def style_lang_facename(style, lang): |
|
|
charshape_id = style['content']['charshape_id'] |
|
|
return docinfo.charshape_lang_facename(charshape_id, lang) |
|
|
|
|
|
def style_lang_facename_name(style, lang): |
|
|
facename = style_lang_facename(style, lang) |
|
|
return facename['content']['name'] |
|
|
|
|
|
self.assertEqual(u'바탕', style_lang_facename_name(styles[0], 'ko')) |
|
|
self.assertEqual(u'한컴돋움', style_lang_facename_name(styles[1], 'ko')) |
|
|
self.assertEqual(u'Times New Roman', |
|
|
style_lang_facename_name(styles[2], 'en')) |
|
|
self.assertEqual(u'Arial', style_lang_facename_name(styles[3], 'en')) |
|
|
self.assertEqual(u'해서 약자', style_lang_facename_name(styles[4], 'cn')) |
|
|
self.assertEqual(u'해서 간자', style_lang_facename_name(styles[5], 'cn')) |
|
|
self.assertEqual(u'명조', style_lang_facename_name(styles[6], 'jp')) |
|
|
self.assertEqual(u'고딕', style_lang_facename_name(styles[7], 'jp')) |
|
|
|
|
|
|
|
|
class BorderFillTest(TestBase): |
|
|
hwp5file_name = 'borderfill.hwp' |
|
|
|
|
|
def test_parse_borderfill(self): |
|
|
|
|
|
docinfo = self.hwp5file.docinfo |
|
|
borderfills = (model for model in docinfo.models() |
|
|
if model['type'] is BorderFill) |
|
|
borderfills = list(borderfills) |
|
|
|
|
|
section = self.hwp5file.bodytext.section(0) |
|
|
tablecells = list(model for model in section.models() |
|
|
if model['type'] is TableCell) |
|
|
for tablecell in tablecells: |
|
|
borderfill_id = tablecell['content']['borderfill_id'] |
|
|
borderfill = borderfills[borderfill_id - 1]['content'] |
|
|
tablecell['borderfill'] = borderfill |
|
|
|
|
|
borderfill = tablecells[0]['borderfill'] |
|
|
self.assertEqual(0, borderfill['fillflags']) |
|
|
self.assertEqual(None, borderfill.get('fill_colorpattern')) |
|
|
self.assertEqual(None, borderfill.get('fill_gradation')) |
|
|
self.assertEqual(None, borderfill.get('fill_image')) |
|
|
|
|
|
borderfill = tablecells[1]['borderfill'] |
|
|
self.assertEqual(1, borderfill['fillflags']) |
|
|
self.assertEqual(dict(background_color=0xff7f3f, |
|
|
pattern_color=0, |
|
|
pattern_type_flags=0xffffffff), |
|
|
borderfill['fill_colorpattern']) |
|
|
self.assertEqual(None, borderfill.get('fill_gradation')) |
|
|
self.assertEqual(None, borderfill.get('fill_image')) |
|
|
|
|
|
borderfill = tablecells[2]['borderfill'] |
|
|
self.assertEqual(4, borderfill['fillflags']) |
|
|
self.assertEqual(None, borderfill.get('fill_colorpattern')) |
|
|
self.assertEqual(dict(blur=40, center=dict(x=0, y=0), |
|
|
colors=[0xff7f3f, 0], |
|
|
shear=90, type=1), |
|
|
borderfill['fill_gradation']) |
|
|
self.assertEqual(None, borderfill.get('fill_image')) |
|
|
|
|
|
borderfill = tablecells[3]['borderfill'] |
|
|
self.assertEqual(2, borderfill['fillflags']) |
|
|
self.assertEqual(None, borderfill.get('fill_colorpattern')) |
|
|
self.assertEqual(None, borderfill.get('fill_gradation')) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
borderfill.get('fill_image')) |
|
|
|
|
|
borderfill = tablecells[4]['borderfill'] |
|
|
self.assertEqual(3, borderfill['fillflags']) |
|
|
self.assertEqual(dict(background_color=0xff7f3f, |
|
|
pattern_color=0, |
|
|
pattern_type_flags=0xffffffff), |
|
|
borderfill['fill_colorpattern']) |
|
|
self.assertEqual(None, borderfill.get('fill_gradation')) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
borderfill.get('fill_image')) |
|
|
|
|
|
borderfill = tablecells[5]['borderfill'] |
|
|
self.assertEqual(6, borderfill['fillflags']) |
|
|
self.assertEqual(None, borderfill.get('fill_colorpattern')) |
|
|
self.assertEqual(dict(blur=40, center=dict(x=0, y=0), |
|
|
colors=[0xff7f3f, 0], |
|
|
shear=90, type=1), |
|
|
borderfill['fill_gradation']) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
borderfill.get('fill_image')) |
|
|
|
|
|
|
|
|
class StyleTest(TestBase): |
|
|
hwp5file_name = 'charstyle.hwp' |
|
|
|
|
|
def test_charstyle(self): |
|
|
|
|
|
docinfo = self.hwp5file.docinfo |
|
|
styles = (model for model in docinfo.models() |
|
|
if model['type'] is Style) |
|
|
styles = list(styles) |
|
|
|
|
|
style = styles[0]['content'] |
|
|
self.assertEqual(dict(name='Normal', |
|
|
unknown=0, |
|
|
parashape_id=0, |
|
|
charshape_id=1, |
|
|
next_style_id=0, |
|
|
lang_id=1042, |
|
|
flags=0, |
|
|
local_name=u'바탕글'), |
|
|
style) |
|
|
charstyle = styles[13]['content'] |
|
|
self.assertEqual(dict(name='', |
|
|
unknown=0, |
|
|
parashape_id=0, |
|
|
charshape_id=1, |
|
|
next_style_id=0, |
|
|
lang_id=1042, |
|
|
flags=1, |
|
|
local_name=u'글자스타일'), |
|
|
charstyle) |
|
|
|
|
|
|
|
|
class ParaCharShapeTest(TestBase): |
|
|
|
|
|
@property |
|
|
def paracharshape_record(self): |
|
|
return self.bodytext.section(0).record(2) |
|
|
|
|
|
def test_read_paracharshape(self): |
|
|
parent_context = dict() |
|
|
parent_model = dict(content=dict(charshapes=5)) |
|
|
|
|
|
record = self.paracharshape_record |
|
|
context = init_record_parsing_context(dict(), record) |
|
|
context['parent'] = parent_context, parent_model |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
self.assertEqual(dict(charshapes=[(0, 7), (19, 8), (23, 7), (24, 9), |
|
|
(26, 7)]), |
|
|
model['content']) |
|
|
|
|
|
|
|
|
class TableTest(TestBase): |
|
|
|
|
|
@property |
|
|
def stream(self): |
|
|
return BytesIO(b'G\x04\xc0\x02 lbt\x11#*\x08\x00\x00\x00\x00\x00\x00' |
|
|
b'\x00\x00\x06\x9e\x00\x00D\x10\x00\x00\x00\x00\x00\x00' |
|
|
b'\x1b\x01\x1b\x01\x1b\x01\x1b\x01\xed\xad\xa2V\x00\x00' |
|
|
b'\x00\x00') |
|
|
|
|
|
@cached_property |
|
|
def tablecontrol_record(self): |
|
|
return self.bodytext.section(0).record(30) |
|
|
|
|
|
@cached_property |
|
|
def tablecaption_record(self): |
|
|
return self.bodytext.section(0).record(68) |
|
|
|
|
|
@cached_property |
|
|
def tablebody_record(self): |
|
|
return self.bodytext.section(0).record(31) |
|
|
|
|
|
@cached_property |
|
|
def tablecell_record(self): |
|
|
return self.bodytext.section(0).record(32) |
|
|
|
|
|
def testParsePass1(self): |
|
|
record = next(read_records(self.stream)) |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
self.assertTrue(TableControl, model['type']) |
|
|
self.assertEqual(1453501933, model['content']['instance_id']) |
|
|
self.assertEqual(0x0, model['content']['x']) |
|
|
self.assertEqual(0x0, model['content']['y']) |
|
|
self.assertEqual(0x1044, model['content']['height']) |
|
|
self.assertEqual(0x9e06, model['content']['width']) |
|
|
self.assertEqual(0, model['content']['unknown1']) |
|
|
self.assertEqual(0x82a2311, model['content']['flags']) |
|
|
self.assertEqual(0, model['content']['z_order']) |
|
|
self.assertEqual(dict(left=283, right=283, top=283, bottom=283), |
|
|
model['content']['margin']) |
|
|
self.assertEqual('tbl ', model['content']['chid']) |
|
|
|
|
|
def test_parse_child_table_body(self): |
|
|
record = self.tablecontrol_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
|
|
|
tablebody_record = self.tablebody_record |
|
|
child_context = init_record_parsing_context(testcontext, |
|
|
tablebody_record) |
|
|
child_model = dict(type=TableBody, content=dict()) |
|
|
child = (child_context, child_model) |
|
|
|
|
|
self.assertFalse(context.get('seen_table_body')) |
|
|
TableControl.on_child(dict(), context, child) |
|
|
|
|
|
|
|
|
self.assertTrue(context['seen_table_body']) |
|
|
|
|
|
self.assertEqual(dict(), child_model['content']) |
|
|
|
|
|
def test_parse_child_table_cell(self): |
|
|
record = self.tablecontrol_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
context['seen_table_body'] = True |
|
|
|
|
|
child_record = self.tablecell_record |
|
|
child_context = init_record_parsing_context(testcontext, child_record) |
|
|
child_model = child_record |
|
|
child_context['parent'] = context, model |
|
|
parse_model(child_context, child_model) |
|
|
self.assertEqual(TableCell, child_model['type']) |
|
|
self.assertEqual(TableCell, child_model['type']) |
|
|
self.assertEqual(dict(padding=dict(top=141, right=141, bottom=141, |
|
|
left=141), |
|
|
rowspan=1, |
|
|
colspan=1, |
|
|
borderfill_id=1, |
|
|
height=282, |
|
|
listflags=32, |
|
|
width=20227, |
|
|
unknown1=0, |
|
|
unknown_width=20227, |
|
|
paragraphs=1, |
|
|
col=0, |
|
|
row=0), child_model['content']) |
|
|
self.assertEqual(b'', child_context['stream'].read()) |
|
|
|
|
|
def test_parse_child_table_caption(self): |
|
|
record = self.tablecontrol_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
context['seen_table_body'] = False |
|
|
|
|
|
child_record = self.tablecaption_record |
|
|
child_context = init_record_parsing_context(testcontext, child_record) |
|
|
child_context['parent'] = context, model |
|
|
child_model = child_record |
|
|
parse_model(child_context, child_model) |
|
|
self.assertEqual(TableCaption, child_model['type']) |
|
|
self.assertEqual(dict(listflags=0, |
|
|
width=8504, |
|
|
max_width=40454, |
|
|
unknown1=0, |
|
|
flags=3, |
|
|
separation=850, |
|
|
paragraphs=2), child_model['content']) |
|
|
self.assertEqual(b'', child_context['stream'].read()) |
|
|
|
|
|
|
|
|
class ShapeComponentTest(TestBase): |
|
|
|
|
|
hwp5file_name = 'textbox.hwp' |
|
|
|
|
|
@cached_property |
|
|
def control_gso_record(self): |
|
|
return self.bodytext.section(0).record(12) |
|
|
|
|
|
@cached_property |
|
|
def shapecomponent_record(self): |
|
|
return self.bodytext.section(0).record(19) |
|
|
|
|
|
@cached_property |
|
|
def textbox_paragraph_list_record(self): |
|
|
return self.bodytext.section(0).record(20) |
|
|
|
|
|
def test_parse_shapecomponent_textbox_paragraph_list(self): |
|
|
record = self.shapecomponent_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
model['type'] = ShapeComponent |
|
|
|
|
|
child_record = self.textbox_paragraph_list_record |
|
|
child_context = init_record_parsing_context(testcontext, |
|
|
child_record) |
|
|
child_context['parent'] = context, model |
|
|
child_model = child_record |
|
|
parse_model(child_context, child_model) |
|
|
self.assertEqual(TextboxParagraphList, child_model['type']) |
|
|
self.assertEqual(dict(listflags=32, |
|
|
padding=dict(top=283, right=283, bottom=283, |
|
|
left=283), |
|
|
unknown1=0, |
|
|
maxwidth=11763, |
|
|
paragraphs=1), child_model['content']) |
|
|
self.assertEqual(b'', child_context['stream'].read()) |
|
|
|
|
|
def test_parse(self): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parent_model = dict(type=GShapeObjectControl) |
|
|
|
|
|
record = self.shapecomponent_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
context['parent'] = dict(), parent_model |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
self.assertEqual(model['type'], ShapeComponent) |
|
|
self.assertTrue('chid0' in model['content']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_rect_fill(self): |
|
|
self.hwp5file_name = 'shapecomponent-rect-fill.hwp' |
|
|
|
|
|
section = self.hwp5file_bin.bodytext.section(0) |
|
|
shapecomps = (model for model in section.models() |
|
|
if model['type'] is ShapeComponent) |
|
|
shapecomps = list(shapecomps) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_image) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_image) |
|
|
self.assertEqual(dict(background_color=0xff7f3f, |
|
|
pattern_color=0, |
|
|
pattern_type_flags=0xffffffff), |
|
|
shapecomp['fill_colorpattern']) |
|
|
self.assertEqual(None, shapecomp.get('fill_gradation')) |
|
|
self.assertEqual(None, shapecomp.get('fill_image')) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_image) |
|
|
self.assertEqual(None, shapecomp.get('fill_colorpattern')) |
|
|
self.assertEqual(dict(type=1, shear=90, |
|
|
center=dict(x=0, y=0), |
|
|
colors=[0xff7f3f, 0], |
|
|
blur=50), shapecomp['fill_gradation']) |
|
|
self.assertEqual(None, shapecomp.get('fill_image')) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_image) |
|
|
self.assertEqual(None, shapecomp.get('fill_colorpattern')) |
|
|
self.assertEqual(None, shapecomp.get('fill_gradation')) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
shapecomp['fill_image']) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_image) |
|
|
self.assertEqual(dict(background_color=0xff7f3f, |
|
|
pattern_color=0, |
|
|
pattern_type_flags=0xffffffff), |
|
|
shapecomp['fill_colorpattern']) |
|
|
self.assertEqual(None, shapecomp.get('fill_gradation')) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
shapecomp['fill_image']) |
|
|
|
|
|
shapecomp = shapecomps.pop(0)['content'] |
|
|
self.assertFalse(shapecomp['fill_flags'].fill_colorpattern) |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_gradation) |
|
|
self.assertTrue(shapecomp['fill_flags'].fill_image) |
|
|
self.assertEqual(None, shapecomp.get('fill_colorpattern')) |
|
|
self.assertEqual(dict(type=1, shear=90, |
|
|
center=dict(x=0, y=0), |
|
|
colors=[0xff7f3f, 0], |
|
|
blur=50), shapecomp['fill_gradation']) |
|
|
self.assertEqual(dict(flags=5, bindata_id=1, effect=0, brightness=0, |
|
|
contrast=0), |
|
|
shapecomp['fill_image']) |
|
|
|
|
|
def test_colorpattern_gradation(self): |
|
|
|
|
|
records = \ |
|
|
[{'level': 1, |
|
|
'payload': b' osg\x10bj\x04\x00\x00\x00\x00\x0c\x00\x00\x004\xbc\x00\x00l\x06\x01\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\xechf\x00\x00\x00\x00', |
|
|
'seqno': 12, |
|
|
'size': 44, |
|
|
'tagid': 71, |
|
|
'tagname': 'HWPTAG_CTRL_HEADER'}, |
|
|
{'level': 2, |
|
|
'payload': b'noc$noc$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x004\xbc\x00\x00l\x06\x01\x004\xbc\x00\x00l\x06\x01\x00\x00\x00\x03\x00\x00\x00w\x00WPS \xb9\xae\x01\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00lop$noc$', |
|
|
'seqno': 13, |
|
|
'size': 206, |
|
|
'tagid': 76, |
|
|
'tagname': 'HWPTAG_SHAPE_COMPONENT'}, |
|
|
{'level': 3, |
|
|
'payload': b'lop$\x00\x00\x00\x00\xcc\x1b\x00\x00\x01\x00\x01\x004\xbc\x00\x00\xa0\xea\x00\x004\xbc\x00\x00\xa0\xea\x00\x00\x00\x00\x00\x00\x10\x00\x1a^\x00\x00Pu\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\xcc\xbb@\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe4\x00\x00\xc8\x00\x00\x00\x01\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\x00\x00', |
|
|
'seqno': 14, |
|
|
'size': 309, |
|
|
'tagid': 76, |
|
|
'tagname': 'HWPTAG_SHAPE_COMPONENT'}, |
|
|
{'level': 4, |
|
|
'payload': b'\x06\x00\x00\x00`,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\xea\x00\x000\xbc\x00\x00\x9c\xea\x00\x000\xbc\x00\x00\x00\x00\x00\x00\xac\x8a\x00\x00\x00\x00\x00\x00', |
|
|
'seqno': 15, |
|
|
'size': 52, |
|
|
'tagid': 82, |
|
|
'tagname': 'HWPTAG_SHAPE_COMPONENT_POLYGON'}, |
|
|
{'level': 3, |
|
|
'payload': b'noc$\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00tb\x00\x00\xf4-\x00\x00tb\x00\x00\xf4-\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00cer$cer$cer$cer$cer$', |
|
|
'seqno': 16, |
|
|
'size': 310, |
|
|
'tagid': 76, |
|
|
'tagname': 'HWPTAG_SHAPE_COMPONENT'}, |
|
|
{'level': 4, |
|
|
'payload': b'cer$\xd8,\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x04\x0f\x00\x00\xf4-\x00\x00\x04\x0f\x00\x00\xf4-\x00\x00\x00\x00\x00\x01\x00\x00\x82\x07\x00\x00\xfa\x16\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00l\xc6@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\xc8\x00\x00\x00\x00\x00\x00\xd1\x00\x05\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x01\xb4\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x002\x00\x00\x00\x02\x00\x00\x00\x00\xfc\x00\x00\xe0\xfc\xc8\x00\x01\x00\x00\x002', |
|
|
'seqno': 17, |
|
|
'size': 447, |
|
|
'tagid': 76, |
|
|
'tagname': 'HWPTAG_SHAPE_COMPONENT'}] |
|
|
|
|
|
context = dict(version=(5, 0, 0, 5)) |
|
|
models = parse_models(context, records) |
|
|
models = list(models) |
|
|
self.assertEqual(1280, models[-1]['content']['fill_flags']) |
|
|
colorpattern = models[-1]['content']['fill_colorpattern'] |
|
|
gradation = models[-1]['content']['fill_gradation'] |
|
|
self.assertEqual(32768, colorpattern['background_color']) |
|
|
self.assertEqual(0, colorpattern['pattern_color']) |
|
|
self.assertEqual(0xffffffff, colorpattern['pattern_type_flags']) |
|
|
|
|
|
self.assertEqual(50, gradation['blur']) |
|
|
self.assertEqual(dict(x=0, y=100), gradation['center']) |
|
|
self.assertEqual([64512, 13171936], gradation['colors']) |
|
|
self.assertEqual(180, gradation['shear']) |
|
|
self.assertEqual(1, gradation['type']) |
|
|
self.assertEqual(1, models[-1]['content']['fill_shape']) |
|
|
self.assertEqual(50, models[-1]['content']['fill_blur_center']) |
|
|
|
|
|
def test_colorpattern_gradation_5017(self): |
|
|
fixturename = '5017-shapecomponent-with-colorpattern-and-gradation.bin' |
|
|
f = self.open_fixture(fixturename, 'rb') |
|
|
try: |
|
|
records = list(read_records(f)) |
|
|
finally: |
|
|
f.close() |
|
|
|
|
|
context = dict(version=(5, 0, 1, 7)) |
|
|
models = parse_models(context, records) |
|
|
models = list(models) |
|
|
self.assertEqual(1280, models[-1]['content']['fill_flags']) |
|
|
colorpattern = models[-1]['content']['fill_colorpattern'] |
|
|
gradation = models[-1]['content']['fill_gradation'] |
|
|
self.assertEqual(32768, colorpattern['background_color']) |
|
|
self.assertEqual(0, colorpattern['pattern_color']) |
|
|
self.assertEqual(0xffffffff, colorpattern['pattern_type_flags']) |
|
|
|
|
|
self.assertEqual(50, gradation['blur']) |
|
|
self.assertEqual(dict(x=0, y=100), gradation['center']) |
|
|
self.assertEqual([64512, 13171936], gradation['colors']) |
|
|
self.assertEqual(180, gradation['shear']) |
|
|
self.assertEqual(1, gradation['type']) |
|
|
self.assertEqual(1, models[-1]['content']['fill_shape']) |
|
|
self.assertEqual(50, models[-1]['content']['fill_blur_center']) |
|
|
|
|
|
|
|
|
class HeaderFooterTest(TestBase): |
|
|
|
|
|
hwp5file_name = 'headerfooter.hwp' |
|
|
|
|
|
@cached_property |
|
|
def header_record(self): |
|
|
return self.bodytext.section(0).record(16) |
|
|
|
|
|
@cached_property |
|
|
def header_paragraph_list_record(self): |
|
|
return self.bodytext.section(0).record(17) |
|
|
|
|
|
def test_parse_child(self): |
|
|
record = self.header_record |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
child_record = self.header_paragraph_list_record |
|
|
child_context = init_record_parsing_context(testcontext, |
|
|
child_record) |
|
|
child_context['parent'] = context, model |
|
|
child_model = child_record |
|
|
parse_model(child_context, child_model) |
|
|
self.assertEqual(HeaderParagraphList, child_model['type']) |
|
|
self.assertEqual(dict(textrefsbitmap=0, |
|
|
numberrefsbitmap=0, |
|
|
height=4252, |
|
|
listflags=0, |
|
|
width=42520, |
|
|
unknown1=0, |
|
|
paragraphs=1), child_model['content']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ListHeaderTest(TestCase): |
|
|
ctx = TestContext() |
|
|
record_bytes = (b'H\x08`\x02\x01\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00' |
|
|
b'\x01\x00\x01\x00\x03O\x00\x00\x1a\x01\x00\x00\x8d\x00' |
|
|
b'\x8d\x00\x8d\x00\x8d\x00\x01\x00\x03O\x00\x00') |
|
|
stream = BytesIO(record_bytes) |
|
|
|
|
|
def testParse(self): |
|
|
record = next(read_records(self.stream)) |
|
|
context = init_record_parsing_context(testcontext, record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
|
|
|
self.assertEqual(ListHeader, model['type']) |
|
|
self.assertEqual(1, model['content']['paragraphs']) |
|
|
self.assertEqual(0x20, model['content']['listflags']) |
|
|
self.assertEqual(0, model['content']['unknown1']) |
|
|
self.assertEqual(8, context['stream'].tell()) |
|
|
|
|
|
|
|
|
class TableBodyTest(TestCase): |
|
|
ctx = TestContext(version=(5, 0, 1, 7)) |
|
|
stream = BytesIO(b'M\x08\xa0\x01\x06\x00\x00\x04\x02\x00\x02\x00\x00\x00' |
|
|
b'\x8d\x00\x8d\x00\x8d\x00\x8d\x00\x02\x00\x02\x00\x01' |
|
|
b'\x00\x00\x00') |
|
|
|
|
|
def test_parse_model(self): |
|
|
record = next(read_records(self.stream)) |
|
|
context = init_record_parsing_context(self.ctx, record) |
|
|
model = record |
|
|
|
|
|
parse_model(context, model) |
|
|
model_type = model['type'] |
|
|
model_content = model['content'] |
|
|
|
|
|
self.assertEqual(TableBody, model_type) |
|
|
self.assertEqual(dict(left=141, right=141, top=141, bottom=141), |
|
|
model_content['padding']) |
|
|
self.assertEqual(0x4000006, model_content['flags']) |
|
|
self.assertEqual(2, model_content['cols']) |
|
|
self.assertEqual(2, model_content['rows']) |
|
|
self.assertEqual(1, model_content['borderfill_id']) |
|
|
self.assertEqual([2, 2], model_content['rowcols']) |
|
|
self.assertEqual(0, model_content['cellspacing']) |
|
|
self.assertEqual([], model_content['validZones']) |
|
|
|
|
|
|
|
|
class Pass2Test(TestCase): |
|
|
ctx = TestContext() |
|
|
|
|
|
def test_pass2_events(self): |
|
|
|
|
|
def items(): |
|
|
yield Record(HWPTAG_BEGIN + 4, 0, ''), |
|
|
yield Record(HWPTAG_BEGIN + 3, 1, ''), |
|
|
yield Record(HWPTAG_BEGIN + 2, 0, ''), |
|
|
yield Record(HWPTAG_BEGIN + 1, 0, ''), |
|
|
items = list(item for item in items()) |
|
|
leveld_items = zip([0, 1, 0, 0], items) |
|
|
|
|
|
events = list(prefix_event(leveld_items)) |
|
|
|
|
|
def expected(): |
|
|
yield STARTEVENT, items[0] |
|
|
yield STARTEVENT, items[1] |
|
|
yield ENDEVENT, items[1] |
|
|
yield ENDEVENT, items[0] |
|
|
yield STARTEVENT, items[2] |
|
|
yield ENDEVENT, items[2] |
|
|
yield STARTEVENT, items[3] |
|
|
yield ENDEVENT, items[3] |
|
|
expected = list(expected()) |
|
|
self.assertEqual(expected, events) |
|
|
|
|
|
|
|
|
class LineSegTest(TestCase): |
|
|
def testDecode(self): |
|
|
data = ('00000000481e0000e8030000e80300005203000058020000dc0500003ca00' |
|
|
'000000006003300000088240000e8030000e80300005203000058020000dc' |
|
|
'0500003ca000000000060067000000c82a0000e8030000e80300005203000' |
|
|
'058020000dc0500003ca0000000000600') |
|
|
data = binascii.a2b_hex(data) |
|
|
lines = list(ParaLineSegList.decode(dict(), data)) |
|
|
self.assertEqual(0, lines[0]['chpos']) |
|
|
self.assertEqual(51, lines[1]['chpos']) |
|
|
self.assertEqual(103, lines[2]['chpos']) |
|
|
|
|
|
|
|
|
class TableCaptionCellTest(TestCase): |
|
|
ctx = TestContext(version=(5, 0, 1, 7)) |
|
|
records_bytes = (b'G\x04\xc0\x02 lbt\x10#*(\x00\x00\x00\x00\x00\x00\x00\x00' |
|
|
b'\x06\x9e\x00\x00\x04\n\x00\x00\x03\x00\x00\x00\x1b\x01R' |
|
|
b'\x037\x02n\x04\n^\xc0V\x00\x00\x00\x00H\x08`\x01\x02\x00' |
|
|
b'\x00\x00\x00\x00\x00\x00\x03\x00\x00\x008!\x00\x00R\x03' |
|
|
b'\x06\x9e\x00\x00M\x08\xa0\x01\x06\x00\x00\x04\x02\x00' |
|
|
b'\x02\x00\x00\x00\x8d\x00\x8d\x00\x8d\x00\x8d\x00\x02\x00' |
|
|
b'\x02\x00\x01\x00\x00\x00H\x08`\x02\x01\x00\x00\x00 \x00' |
|
|
b'\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x03O\x00\x00' |
|
|
b'\x1a\x01\x00\x00\x8d\x00\x8d\x00\x8d\x00\x8d\x00\x01\x00' |
|
|
b'\x03O\x00\x00') |
|
|
|
|
|
def testParsePass1(self): |
|
|
stream = BytesIO(self.records_bytes) |
|
|
records = list(read_records(stream)) |
|
|
result = list(parse_models_intern(self.ctx, records)) |
|
|
|
|
|
tablecaption = result[1] |
|
|
context, model = tablecaption |
|
|
model_type = model['type'] |
|
|
model_content = model['content'] |
|
|
stream = context['stream'] |
|
|
|
|
|
self.assertEqual(TableCaption, model_type) |
|
|
self.assertEqual(22, stream.tell()) |
|
|
|
|
|
self.assertEqual(2, model_content['paragraphs']) |
|
|
self.assertEqual(0x0, model_content['listflags']) |
|
|
self.assertEqual(0, model_content['unknown1']) |
|
|
|
|
|
self.assertEqual(3, model_content['flags']) |
|
|
self.assertEqual(8504, model_content['width']) |
|
|
self.assertEqual(850, model_content['separation']) |
|
|
self.assertEqual(40454, model_content['max_width']) |
|
|
|
|
|
tablecell = result[3] |
|
|
context, model = tablecell |
|
|
model_type = model['type'] |
|
|
model_content = model['content'] |
|
|
stream = context['stream'] |
|
|
self.assertEqual(TableCell, model_type) |
|
|
self.assertEqual(38, stream.tell()) |
|
|
|
|
|
self.assertEqual(1, model_content['paragraphs']) |
|
|
self.assertEqual(0x20, model_content['listflags']) |
|
|
self.assertEqual(0, model_content['unknown1']) |
|
|
|
|
|
self.assertEqual(0, model_content['col']) |
|
|
self.assertEqual(0, model_content['row']) |
|
|
self.assertEqual(1, model_content['colspan']) |
|
|
self.assertEqual(1, model_content['rowspan']) |
|
|
self.assertEqual(0x4f03, model_content['width']) |
|
|
self.assertEqual(0x11a, model_content['height']) |
|
|
self.assertEqual(dict(left=141, right=141, top=141, bottom=141), |
|
|
model_content['padding']) |
|
|
self.assertEqual(1, model_content['borderfill_id'],) |
|
|
self.assertEqual(0x4f03, model_content['unknown_width']) |
|
|
|
|
|
|
|
|
class TestRecordModel(TestCase): |
|
|
def test_assign_enum_flags_name(self): |
|
|
|
|
|
class FooRecord(RecordModel): |
|
|
Bar = Flags(UINT32) |
|
|
Baz = Enum() |
|
|
self.assertEqual('Bar', FooRecord.Bar.__name__) |
|
|
self.assertEqual('Baz', FooRecord.Baz.__name__) |
|
|
|
|
|
|
|
|
class TestControlType(TestCase): |
|
|
def test_ControlType(self): |
|
|
|
|
|
class FooControl(Control): |
|
|
chid = 'foo!' |
|
|
try: |
|
|
class Foo2Control(Control): |
|
|
chid = 'foo!' |
|
|
except Exception: |
|
|
pass |
|
|
else: |
|
|
assert False, 'Exception expected' |
|
|
|
|
|
|
|
|
class TestControlChar(TestBase): |
|
|
|
|
|
def test_decode(self): |
|
|
paratext_record = self.hwp5file.bodytext.section(0).record(1) |
|
|
payload = paratext_record['payload'] |
|
|
controlchar = ControlChar.decode(payload[0:16]) |
|
|
self.assertEqual(dict(code=ord(ControlChar.SECTION_COLUMN_DEF), |
|
|
chid='secd', |
|
|
param=b'\x00' * 8), controlchar) |
|
|
|
|
|
def test_find(self): |
|
|
bytes = b'\x41\x00' |
|
|
self.assertEqual((2, 2), ControlChar.find(bytes, 0)) |
|
|
|
|
|
def test_tab(self): |
|
|
self.hwp5file_name = 'tabdef.hwp' |
|
|
models = self.hwp5file.bodytext.section(0).models() |
|
|
paratexts = list(model for model in models |
|
|
if model['type'] is ParaText) |
|
|
|
|
|
def paratext_tabs(paratext): |
|
|
for range, chunk in paratext['content']['chunks']: |
|
|
if isinstance(chunk, dict): |
|
|
if unichr(chunk['code']) == ControlChar.TAB: |
|
|
yield chunk |
|
|
self.assertEqual(set(['code', 'param']), |
|
|
set(next(paratext_tabs(paratexts[0])).keys())) |
|
|
|
|
|
def paratext_tab_params(paratext): |
|
|
for tab in paratext_tabs(paratext): |
|
|
yield tab['param'] |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(4000, 1)] * 3, |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(2000, 1), (1360, 1), (1360, 1)], |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(2328, 2)] * 3, |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(2646, 3), (2292, 3), (2292, 3)], |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(2104, 4)] * 3, |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(4000, 1), (3360, 1), (3360, 1)], |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(4000, 1), (3328, 1)], |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
tabs = list(paratext_tab_params(paratexts.pop(0))) |
|
|
self.assertEqual([(4000, 1), (3672, 1), (33864, 2)], |
|
|
list((tab['width'], tab['unknown1']) |
|
|
for tab in tabs)) |
|
|
|
|
|
|
|
|
class TestFootnoteShape(TestBase): |
|
|
|
|
|
def test_footnote_shape(self): |
|
|
path = get_fixture_path('footnote-endnote.hwp') |
|
|
hwp5file = Hwp5File(path) |
|
|
|
|
|
models = hwp5file.bodytext.section(0).models() |
|
|
models = list(models) |
|
|
fnshape = models[6] |
|
|
self.assertEqual(850, fnshape['content']['splitter_margin_top']) |
|
|
self.assertEqual(567, fnshape['content']['splitter_margin_bottom']) |
|
|
|
|
|
|
|
|
class TestControlData(TestBase): |
|
|
def test_parse(self): |
|
|
|
|
|
record = \ |
|
|
{'level': 2, |
|
|
'payload': b'\x1b\x02\x01\x00\x00\x00\x00@\x01\x00\x03\x00X\xc7H\xc5\x85\xba', |
|
|
'seqno': 27, |
|
|
'size': 18, |
|
|
'tagid': 87, |
|
|
'tagname': 'HWPTAG_CTRL_DATA'} |
|
|
context = init_record_parsing_context(dict(), record) |
|
|
model = record |
|
|
parse_model(context, model) |
|
|
self.assertEqual(ControlData, model['type']) |
|
|
self.assertEqual(dict(), model['content']) |
|
|
|
|
|
|
|
|
class TestModelJson(TestBase): |
|
|
def test_model_to_json(self): |
|
|
model = self.hwp5file.docinfo.model(0) |
|
|
json_string = model_to_json(model) |
|
|
|
|
|
jsonobject = json.loads(json_string) |
|
|
self.assertEqual('DocumentProperties', jsonobject['type']) |
|
|
|
|
|
def test_model_to_json_should_not_modify_input(self): |
|
|
model = self.hwp5file.docinfo.model(0) |
|
|
model_to_json(model, indent=2, sort_keys=True) |
|
|
self.assertFalse(isinstance(model['type'], basestring)) |
|
|
|
|
|
def test_model_to_json_with_controlchar(self): |
|
|
model = self.hwp5file.bodytext.section(0).model(1) |
|
|
json_string = model_to_json(model) |
|
|
|
|
|
jsonobject = json.loads(json_string) |
|
|
self.assertEqual('ParaText', jsonobject['type']) |
|
|
self.assertEqual([[0, 8], |
|
|
dict(code=2, param='\x00' * 8, chid='secd')], |
|
|
jsonobject['content']['chunks'][0]) |
|
|
|
|
|
def test_model_to_json_with_unparsed(self): |
|
|
|
|
|
model = dict(type=RecordModel, content=[], payload=b'\x00\x01\x02\x03', |
|
|
unparsed=b'\xff\xfe\xfd\xfc') |
|
|
json_string = model_to_json(model) |
|
|
|
|
|
jsonobject = json.loads(json_string) |
|
|
self.assertEqual(['ff fe fd fc'], jsonobject['unparsed']) |
|
|
|
|
|
def test_generate_models_json_array(self): |
|
|
models_json = self.hwp5file.bodytext.section(0).models_json() |
|
|
gen = models_json.generate() |
|
|
|
|
|
json_array = json.loads(''.join(gen)) |
|
|
self.assertEqual(128, len(json_array)) |
|
|
|
|
|
|
|
|
class TestModelStream(TestBase): |
|
|
@cached_property |
|
|
def docinfo(self): |
|
|
return ModelStream(self.hwp5file_rec['DocInfo'], |
|
|
self.hwp5file_rec.header.version) |
|
|
|
|
|
def test_models(self): |
|
|
self.assertEqual(67, len(list(self.docinfo.models()))) |
|
|
|
|
|
def test_models_treegrouped(self): |
|
|
section = self.bodytext.section(0) |
|
|
for idx, paragraph_models in enumerate(section.models_treegrouped()): |
|
|
paragraph_models = list(paragraph_models) |
|
|
leader = paragraph_models[0] |
|
|
|
|
|
self.assertEqual(Paragraph, leader['type']) |
|
|
|
|
|
self.assertEqual(0, leader['level']) |
|
|
|
|
|
|
|
|
def test_model(self): |
|
|
model = self.docinfo.model(0) |
|
|
self.assertEqual(0, model['seqno']) |
|
|
|
|
|
model = self.docinfo.model(10) |
|
|
self.assertEqual(10, model['seqno']) |
|
|
|
|
|
def test_models_json_open(self): |
|
|
f = self.docinfo.models_json().open() |
|
|
f = codecs.getreader('utf-8')(f) |
|
|
try: |
|
|
self.assertEqual(67, len(json.load(f))) |
|
|
finally: |
|
|
f.close() |
|
|
|