You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

990 lines
22 KiB

#include "LoadHandler.h"
#include "model/XojPage.h"
#include "model/BackgroundImage.h"
#include "model/StrokeStyle.h"
#include "LoadHandlerHelper.h"
#include <config.h>
#include <GzUtil.h>
#include <i18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#define error2(var, ...) \
if (var == NULL) \
{ \
var = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, __VA_ARGS__); \
}
#define error(...) \
if (error == NULL) \
{ \
error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, __VA_ARGS__); \
}
LoadHandler::LoadHandler()
: attachedPdfMissing(false),
removePdfBackgroundFlag(false),
pdfReplacementAttach(false),
pdfFilenameParsed(false),
pos(PARSER_POS_NOT_STARTED),
fileversion(0),
fp(NULL),
layer(NULL),
stroke(NULL),
text(NULL),
image(NULL),
teximage(NULL),
attributeNames(NULL),
attributeValues(NULL),
elementName(NULL),
loadedTimeStamp(0),
doc(&dHanlder)
{
XOJ_INIT_TYPE(LoadHandler);
this->error = NULL;
initAttributes();
}
LoadHandler::~LoadHandler()
{
XOJ_RELEASE_TYPE(LoadHandler);
}
void LoadHandler::initAttributes()
{
XOJ_CHECK_TYPE(LoadHandler);
this->fp = NULL;
this->error = NULL;
this->attributeNames = NULL;
this->attributeValues = NULL;
this->elementName = NULL;
this->pdfFilenameParsed = false;
this->attachedPdfMissing = false;
this->page = NULL;
this->layer = NULL;
this->stroke = NULL;
this->image = NULL;
this->teximage = NULL;
this->text = NULL;
}
string LoadHandler::getLastError()
{
XOJ_CHECK_TYPE(LoadHandler);
return this->lastError;
}
bool LoadHandler::isAttachedPdfMissing()
{
XOJ_CHECK_TYPE(LoadHandler);
return this->attachedPdfMissing;
}
string LoadHandler::getMissingPdfFilename()
{
XOJ_CHECK_TYPE(LoadHandler);
return this->pdfMissing;
}
void LoadHandler::removePdfBackground()
{
XOJ_CHECK_TYPE(LoadHandler);
this->removePdfBackgroundFlag = true;
}
void LoadHandler::setPdfReplacement(string filename, bool attachToDocument)
{
XOJ_CHECK_TYPE(LoadHandler);
this->pdfReplacementFilename = filename;
this->pdfReplacementAttach = attachToDocument;
}
bool LoadHandler::openFile(string filename)
{
XOJ_CHECK_TYPE(LoadHandler);
this->filename = filename;
this->fp = GzUtil::openPath(filename, "r");
if (!this->fp)
{
this->lastError = FS(_F("Could not open file: \"{1}\"") % filename);
return false;
}
return true;
}
bool LoadHandler::closeFile()
{
XOJ_CHECK_TYPE(LoadHandler);
return gzclose(this->fp);
}
int LoadHandler::readFile(char* buffer, int len)
{
XOJ_CHECK_TYPE(LoadHandler);
if (gzeof(this->fp))
{
return -1;
}
return gzread(this->fp, buffer, len);
}
bool LoadHandler::parseXml()
{
XOJ_CHECK_TYPE(LoadHandler);
const GMarkupParser parser = { LoadHandler::parserStartElement, LoadHandler::parserEndElement, LoadHandler::parserText, NULL, NULL };
this->error = NULL;
gboolean valid = true;
this->pos = PARSER_POS_NOT_STARTED;
this->creator = "Unknown";
this->fileversion = 1;
GMarkupParseContext* context = g_markup_parse_context_new(&parser, (GMarkupParseFlags) 0, this, NULL);
int len = 0;
do
{
char buffer[1024];
len = readFile(buffer, sizeof(buffer));
if (len > 0)
{
valid = g_markup_parse_context_parse(context, buffer, len, &error);
}
if (error)
{
g_warning("LoadHandler::parseXml: %s\n", error->message);
valid = false;
break;
}
}
while (len >= 0 && valid && !error);
if (valid)
{
valid = g_markup_parse_context_end_parse(context, &error);
}
else
{
if (error != NULL && error->message != NULL)
{
this->lastError = FS(_F("XML Parser error: {1}") % error->message);
g_error_free(error);
}
else
{
this->lastError = _("Unknown parser error");
}
g_warning("LoadHandler::parseXml: %s\n", this->lastError.c_str());
}
g_markup_parse_context_free(context);
if (this->pos != PASER_POS_FINISHED && this->lastError == "")
{
lastError = _("Document is not complete (maybe the end is cut off?)");
return false;
}
doc.setCreateBackupOnSave(this->fileversion >= 3);
return valid;
}
void LoadHandler::parseStart()
{
XOJ_CHECK_TYPE(LoadHandler);
if (strcmp(elementName, "xournal") == 0)
{
endRootTag = "xournal";
// Read the document version
const char* version = LoadHandlerHelper::getAttrib("version", true, this);
if (version)
{
this->creator = "Xournal ";
this->creator += version;
}
const char* fileversion = LoadHandlerHelper::getAttrib("fileversion", true, this);
if (fileversion)
{
this->fileversion = atoi(fileversion);
}
const char* creator = LoadHandlerHelper::getAttrib("creator", true, this);
if (creator)
{
this->creator = creator;
}
this->pos = PARSER_POS_STARTED;
}
else if (strcmp(elementName, "MrWriter") == 0)
{
endRootTag = "MrWriter";
// Read the document version
const char* version = LoadHandlerHelper::getAttrib("version", true, this);
if (version)
{
this->creator = "MrWriter ";
this->creator += version;
}
// Document version 1:
// Handle it the same as a Xournal document, and don't allow to overwrite
this->fileversion = 1;
this->pos = PARSER_POS_STARTED;
}
else
{
error("%s", FC(_F("Unexpected root tag: {1}") % elementName));
}
}
void LoadHandler::parseContents()
{
XOJ_CHECK_TYPE(LoadHandler);
if (strcmp(elementName, "page") == 0)
{
this->pos = PARSER_POS_IN_PAGE;
double width = LoadHandlerHelper::getAttribDouble("width", this);
double height = LoadHandlerHelper::getAttribDouble("height", this);
this->page = new XojPage(width, height);
this->doc.addPage(this->page);
}
else if (strcmp(elementName, "title") == 0)
{
// Ignore this tag, it says nothing...
}
else if (strcmp(elementName, "preview") == 0)
{
// Ignore this tag, we don't need a preview
}
else
{
g_warning("%s", FC(_F("Unexpected tag in document: \"{1}\"") % elementName));
}
}
void LoadHandler::parseBgSolid()
{
XOJ_CHECK_TYPE(LoadHandler);
PageType bg;
const char* style = LoadHandlerHelper::getAttrib("style", false, this);
if (style != NULL)
{
bg.format = style;
}
const char* config = LoadHandlerHelper::getAttrib("config", true, this);
if (config != NULL)
{
bg.config = config;
}
this->page->setBackgroundType(bg);
int color = LoadHandlerHelper::parseBackgroundColor(this);
this->page->setBackgroundColor(color);
}
void LoadHandler::parseBgPixmap()
{
XOJ_CHECK_TYPE(LoadHandler);
const char* domain = LoadHandlerHelper::getAttrib("domain", false, this);
const char* filename = LoadHandlerHelper::getAttrib("filename", false, this);
string fileToLoad;
bool loadFile = false;
if (!strcmp(domain, "absolute"))
{
fileToLoad = filename;
loadFile = true;
}
else if (!strcmp(domain, "attach"))
{
fileToLoad = this->filename;
fileToLoad += ".";
fileToLoad += filename;
loadFile = true;
}
if (loadFile)
{
GError* error = NULL;
BackgroundImage img;
img.loadFile(fileToLoad, &error);
if (error)
{
error("%s", FC(_F("Could not read image: {1}. Error message: {2}") % fileToLoad % error->message));
g_error_free(error);
}
this->page->setBackgroundImage(img);
}
else if (!strcmp(domain, "clone"))
{
PageRef p = doc.getPage(atoi(filename));
if (p.isValid())
{
this->page->setBackgroundImage(p->getBackgroundImage());
}
}
else
{
error("%s", FC(_F("Unknown pixmap::domain type: {1}") % domain));
}
this->page->setBackgroundType(PageType(":image"));
}
void LoadHandler::parseBgPdf()
{
XOJ_CHECK_TYPE(LoadHandler);
int pageno = LoadHandlerHelper::getAttribInt("pageno", this);
bool attachToDocument = false;
string pdfFilename;
this->page->setBackgroundPdfPageNr(pageno - 1);
if (!this->pdfFilenameParsed)
{
if (this->pdfReplacementFilename == "")
{
const char* domain = LoadHandlerHelper::getAttrib("domain", false, this);
const char* sFilename = LoadHandlerHelper::getAttrib("filename", false, this);
if (sFilename == NULL)
{
error("PDF Filename missing!");
return;
}
pdfFilename = sFilename;
if (!strcmp("absolute", domain)) // Absolute OR relative path
{
if (!g_file_test(sFilename, G_FILE_TEST_EXISTS))
{
char* dirname = g_path_get_dirname(xournalFilename.c_str());
char* file = g_path_get_basename(sFilename);
char* tmpFilename = g_build_path(G_DIR_SEPARATOR_S, dirname, file, NULL);
if (g_file_test(tmpFilename, G_FILE_TEST_EXISTS))
{
pdfFilename = tmpFilename;
}
g_free(tmpFilename);
g_free(dirname);
g_free(file);
}
}
else if (!strcmp("attach", domain))
{
attachToDocument = true;
char* tmpFilename = g_strdup_printf("%s.%s", xournalFilename.c_str(), sFilename);
if (g_file_test(tmpFilename, G_FILE_TEST_EXISTS))
{
pdfFilename = tmpFilename;
}
g_free(tmpFilename);
}
else
{
error("%s", FC(_F("Unknown domain type: {1}") % domain));
return;
}
}
else
{
pdfFilename = this->pdfReplacementFilename;
attachToDocument = this->pdfReplacementAttach;
}
this->pdfFilenameParsed = true;
if (g_file_test(pdfFilename.c_str(), G_FILE_TEST_EXISTS))
{
doc.readPdf(pdfFilename, false, attachToDocument);
if (doc.getLastErrorMsg() != "")
{
error("%s", FC(_F("Error reading PDF: {1}") % doc.getLastErrorMsg()));
}
}
else
{
if (attachToDocument)
{
this->attachedPdfMissing = true;
}
else
{
this->pdfMissing = pdfFilename.c_str();
}
}
}
}
void LoadHandler::parsePage()
{
XOJ_CHECK_TYPE(LoadHandler);
if (!strcmp(elementName, "background"))
{
const char* type = LoadHandlerHelper::getAttrib("type", false, this);
if (strcmp("solid", type) == 0)
{
parseBgSolid();
}
else if (strcmp("pixmap", type) == 0)
{
parseBgPixmap();
}
else if (strcmp("pdf", type) == 0)
{
if (this->removePdfBackgroundFlag)
{
this->page->setBackgroundType(PageType("plain"));
this->page->setBackgroundColor(0xffffff);
}
else
{
parseBgPdf();
}
}
else
{
error("%s", FC(_F("Unknown background type: {1}") % type));
}
}
else if (!strcmp(elementName, "layer"))
{
this->pos = PARSER_POS_IN_LAYER;
this->layer = new Layer();
this->page->addLayer(this->layer);
}
}
void LoadHandler::parseStroke()
{
XOJ_CHECK_TYPE(LoadHandler);
this->stroke = new Stroke();
this->layer->addElement(this->stroke);
const char* width = LoadHandlerHelper::getAttrib("width", false, this);
char* endPtr = NULL;
stroke->setWidth(g_ascii_strtod(width, &endPtr));
if (endPtr == width)
{
error("%s", FC(_F("Error reading width of a stroke: {1}") % width));
return;
}
// MrWriter writes pressures as separate field
const char* pressure = LoadHandlerHelper::getAttrib("pressures", true, this);
if (pressure == NULL)
{
// Xournal / Xournal++ uses the width field
pressure = endPtr;
}
while (*pressure != 0)
{
char* tmpptr = NULL;
double val = g_ascii_strtod(pressure, &tmpptr);
if (tmpptr == pressure)
{
break;
}
pressure = tmpptr;
this->pressureBuffer.push_back(val);
}
int color = 0;
const char* sColor = LoadHandlerHelper::getAttrib("color", false, this);
if (!LoadHandlerHelper::parseColor(sColor, color, this))
{
return;
}
stroke->setColor(color);
/** read stroke timestamps (xopp fileformat) */
const char* fn = LoadHandlerHelper::getAttrib("fn", true, this);
if (fn != NULL)
{
stroke->setAudioFilename(fn);
}
if (this->fileversion < 4)
{
int ts = 0;
if (LoadHandlerHelper::getAttribInt("ts", true, this, ts))
{
stroke->setTimestamp(ts * 1000);
}
}
else
{
size_t ts = 0;
if (LoadHandlerHelper::getAttribSizeT("ts", true, this, ts))
{
stroke->setTimestamp(ts);
}
}
int fill = -1;
if (LoadHandlerHelper::getAttribInt("fill", true, this, fill))
{
stroke->setFill(fill);
}
const char* style = LoadHandlerHelper::getAttrib("style", true, this);
if (style != NULL)
{
stroke->setLineStyle(StrokeStyle::parseStyle(style));
}
const char* tool = LoadHandlerHelper::getAttrib("tool", false, this);
if (strcmp("eraser", tool) == 0)
{
stroke->setToolType(STROKE_TOOL_ERASER);
}
else if (strcmp("pen", tool) == 0)
{
stroke->setToolType(STROKE_TOOL_PEN);
}
else if (strcmp("highlighter", tool) == 0)
{
stroke->setToolType(STROKE_TOOL_HIGHLIGHTER);
}
else
{
g_warning("%s", FC(_F("Unknown stroke type: \"{1}\", assuming pen") % tool));
}
/**
* For each stroke being read, set the timestamp value
* we've read just before.
* Afterwards, clean the read timestamp data.
*/
if (loadedFilename.length() != 0)
{
this->stroke->setTimestamp(loadedTimeStamp);
this->stroke->setAudioFilename(loadedFilename);
loadedFilename = "";
loadedTimeStamp = 0;
}
}
void LoadHandler::parseText()
{
XOJ_CHECK_TYPE(LoadHandler);
this->text = new Text();
this->layer->addElement(this->text);
const char* sFont = LoadHandlerHelper::getAttrib("font", false, this);
double fontSize = LoadHandlerHelper::getAttribDouble("size", this);
double x = LoadHandlerHelper::getAttribDouble("x", this);
double y = LoadHandlerHelper::getAttribDouble("y", this);
this->text->setX(x);
this->text->setY(y);
XojFont& f = text->getFont();
f.setName(sFont);
f.setSize(fontSize);
const char* sColor = LoadHandlerHelper::getAttrib("color", false, this);
int color = 0;
LoadHandlerHelper::parseColor(sColor, color, this);
text->setColor(color);
const char* fn = LoadHandlerHelper::getAttrib("fn", true, this);
if (fn != NULL)
{
text->setAudioFilename(fn);
}
size_t ts = 0;
if (LoadHandlerHelper::getAttribSizeT("ts", true, this, ts))
{
text->setTimestamp(ts);
}
}
void LoadHandler::parseImage()
{
XOJ_CHECK_TYPE(LoadHandler);
double left = LoadHandlerHelper::getAttribDouble("left", this);
double top = LoadHandlerHelper::getAttribDouble("top", this);
double right = LoadHandlerHelper::getAttribDouble("right", this);
double bottom = LoadHandlerHelper::getAttribDouble("bottom", this);
this->image = new Image();
this->layer->addElement(this->image);
this->image->setX(left);
this->image->setY(top);
this->image->setWidth(right - left);
this->image->setHeight(bottom - top);
}
void LoadHandler::parseTexImage()
{
XOJ_CHECK_TYPE(LoadHandler);
double left = LoadHandlerHelper::getAttribDouble("left", this);
double top = LoadHandlerHelper::getAttribDouble("top", this);
double right = LoadHandlerHelper::getAttribDouble("right", this);
double bottom = LoadHandlerHelper::getAttribDouble("bottom", this);
const char* imText = LoadHandlerHelper::getAttrib("text", false, this);
const char* compatibilityTest = LoadHandlerHelper::getAttrib("texlength", false, this);
int imTextLen = strlen(imText);
if (compatibilityTest != NULL)
{
imTextLen = LoadHandlerHelper::getAttribInt("texlength", this);
}
this->teximage = new TexImage();
this->layer->addElement(this->teximage);
this->teximage->setX(left);
this->teximage->setY(top);
this->teximage->setWidth(right - left);
this->teximage->setHeight(bottom - top);
this->teximage->setText(string(imText, imTextLen));
}
void LoadHandler::parseLayer()
{
XOJ_CHECK_TYPE(LoadHandler);
/**
* read the timestamp before each stroke.
* Used for backwards compatibility
* against xoj files with timestamps)
**/
if (!strcmp(elementName, "timestamp"))
{
loadedTimeStamp = LoadHandlerHelper::getAttribInt("ts", this);
loadedFilename = LoadHandlerHelper::getAttrib("fn", false, this);
}
if (!strcmp(elementName, "stroke")) // start of a stroke
{
this->pos = PARSER_POS_IN_STROKE;
parseStroke();
}
else if (!strcmp(elementName, "text")) // start of a text item
{
this->pos = PARSER_POS_IN_TEXT;
parseText();
}
else if (!strcmp(elementName, "image")) // start of a image item
{
this->pos = PARSER_POS_IN_IMAGE;
parseImage();
}
else if (!strcmp(elementName, "teximage")) // start of a image item
{
this->pos = PARSER_POS_IN_TEXIMAGE;
parseTexImage();
}
}
void LoadHandler::parserStartElement(GMarkupParseContext* context, const gchar* elementName, const gchar** attributeNames,
const gchar** attributeValues, gpointer userdata, GError** error)
{
LoadHandler* handler = (LoadHandler*) userdata;
// Return on error
if (*error)
{
return;
}
XOJ_CHECK_TYPE_OBJ(handler, LoadHandler);
handler->attributeNames = attributeNames;
handler->attributeValues = attributeValues;
handler->elementName = elementName;
if (handler->pos == PARSER_POS_NOT_STARTED)
{
handler->parseStart();
}
else if (handler->pos == PARSER_POS_STARTED)
{
handler->parseContents();
}
else if (handler->pos == PARSER_POS_IN_PAGE)
{
handler->parsePage();
}
else if (handler->pos == PARSER_POS_IN_LAYER)
{
handler->parseLayer();
}
handler->attributeNames = NULL;
handler->attributeValues = NULL;
handler->elementName = NULL;
}
void LoadHandler::parserEndElement(GMarkupParseContext* context, const gchar* elementName,
gpointer userdata, GError** error)
{
// Return on error
if (*error)
{
return;
}
LoadHandler* handler = (LoadHandler*) userdata;
XOJ_CHECK_TYPE_OBJ(handler, LoadHandler);
if (handler->pos == PARSER_POS_STARTED && strcmp(elementName, handler->endRootTag) == 0)
{
handler->pos = PASER_POS_FINISHED;
}
else if (handler->pos == PARSER_POS_IN_PAGE && strcmp(elementName, "page") == 0)
{
handler->pos = PARSER_POS_STARTED;
handler->page = NULL;
}
else if (handler->pos == PARSER_POS_IN_LAYER && strcmp(elementName, "layer") == 0)
{
handler->pos = PARSER_POS_IN_PAGE;
handler->layer = NULL;
}
else if (handler->pos == PARSER_POS_IN_LAYER && strcmp(elementName, "timestamp") == 0)
{
/** Used for backwards compatibility against xoj files with timestamps) */
handler->pos = PARSER_POS_IN_LAYER;
handler->stroke = NULL;
}
else if (handler->pos == PARSER_POS_IN_STROKE && strcmp(elementName, "stroke") == 0)
{
handler->pos = PARSER_POS_IN_LAYER;
handler->stroke = NULL;
}
else if (handler->pos == PARSER_POS_IN_TEXT && strcmp(elementName, "text") == 0)
{
handler->pos = PARSER_POS_IN_LAYER;
handler->text = NULL;
}
else if (handler->pos == PARSER_POS_IN_IMAGE && strcmp(elementName, "image") == 0)
{
handler->pos = PARSER_POS_IN_LAYER;
handler->image = NULL;
}
else if (handler->pos == PARSER_POS_IN_TEXIMAGE && strcmp(elementName, "teximage") == 0)
{
handler->pos = PARSER_POS_IN_LAYER;
handler->teximage = NULL;
}
}
void LoadHandler::parserText(GMarkupParseContext* context, const gchar* text,
gsize textLen, gpointer userdata, GError** error)
{
// Return on error
if (*error)
{
return;
}
LoadHandler* handler = (LoadHandler*) userdata;
XOJ_CHECK_TYPE_OBJ(handler, LoadHandler);
if (handler->pos == PARSER_POS_IN_STROKE)
{
const char* ptr = text;
int n = 0;
bool xRead = false;
double x = 0;
while (textLen > 0)
{
double tmp = g_ascii_strtod(text, (char**) (&ptr));
if (ptr == text)
{
break;
}
textLen -= (ptr - text);
text = ptr;
n++;
if (!xRead)
{
xRead = true;
x = tmp;
}
else
{
xRead = false;
handler->stroke->addPoint(Point(x, tmp));
}
}
handler->stroke->freeUnusedPointItems();
if (n < 4 || (n & 1))
{
error2(*error, "%s", FC(_F("Wrong count of points ({1})") % n));
return;
}
if (handler->pressureBuffer.size() != 0)
{
if ((int)handler->pressureBuffer.size() >= handler->stroke->getPointCount() - 1)
{
handler->stroke->setPressure(handler->pressureBuffer);
handler->pressureBuffer.clear();
}
else
{
g_warning("%s", FC(_F("xoj-File: {1}") % handler->filename));
g_warning("%s", FC(_F("Wrong number of points, got {1}, expected {2}")
% handler->pressureBuffer.size() % (handler->stroke->getPointCount() - 1)));
}
}
handler->pressureBuffer.clear();
}
else if (handler->pos == PARSER_POS_IN_TEXT)
{
gchar* txt = g_strndup(text, textLen);
handler->text->setText(txt);
g_free(txt);
}
else if (handler->pos == PARSER_POS_IN_IMAGE)
{
handler->readImage(text, textLen);
}
else if (handler->pos == PARSER_POS_IN_TEXIMAGE)
{
handler->readTexImage(text, textLen);
}
}
void LoadHandler::readImage(const gchar* base64_str, gsize base64_strlen)
{
XOJ_CHECK_TYPE(LoadHandler);
gsize png_buflen;
// We have to copy the string in order to null terminate it, sigh.
gchar* base64_str2 = (gchar*) g_memdup(base64_str, base64_strlen + 1);
base64_str2[base64_strlen] = '\0';
guchar* png_buf = g_base64_decode(base64_str2, &png_buflen);
g_free(base64_str2);
this->image->setImage(string((char*)png_buf, png_buflen));
g_free(png_buf);
}
void LoadHandler::readTexImage(const gchar* base64_str, gsize base64_strlen)
{
XOJ_CHECK_TYPE(LoadHandler);
gsize png_buflen;
// We have to copy the string in order to null terminate it, sigh.
gchar* base64_str2 = (gchar*) g_memdup(base64_str, base64_strlen + 1);
base64_str2[base64_strlen] = '\0';
guchar* png_buf = g_base64_decode(base64_str2, &png_buflen);
g_free(base64_str2);
this->teximage->setImage(string((char*)png_buf, png_buflen));
g_free(png_buf);
}
/**
* Document should not be freed, it will be freed with LoadHandler!
*/
Document* LoadHandler::loadDocument(string filename)
{
XOJ_CHECK_TYPE(LoadHandler);
initAttributes();
doc.clearDocument();
if (!openFile(filename))
{
return NULL;
}
xournalFilename = filename;
this->pdfFilenameParsed = false;
if (!parseXml())
{
closeFile();
return NULL;
}
if (fileversion == 1)
{
// This is a Xournal document, not a Xournal++
// Even if the new fileextension is .xopp, allow to
// overwrite .xoj files which are created by Xournal++
// Force the user to save is a bad idea, this will annoy the user
// Rename files is also not that user friendly.
doc.setFilename("");
}
else
{
doc.setFilename(filename.c_str());
}
closeFile();
return &this->doc;
}