/* * Copyright 2008-2009, 2023, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * François Revol * Zardshard */ #include "StyledTextImporter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Defines.h" #include "Icon.h" #include "PathSourceShape.h" #include "Style.h" #include "VectorPath.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyledTextImport" //#define CALLED() printf("%s()\n", __FUNCTION__); #define CALLED() do {} while (0) using std::nothrow; class ShapeIterator : public BShapeIterator { public: ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset, const char *name); ~ShapeIterator() {}; virtual status_t IterateMoveTo(BPoint *point); virtual status_t IterateLineTo(int32 lineCount, BPoint *linePts); virtual status_t IterateBezierTo(int32 bezierCount, BPoint *bezierPts); virtual status_t IterateClose(); private: VectorPath *CurrentPath(); void NextPath(); Icon *fIcon; PathSourceShape *fShape; VectorPath *fPath; BPoint fOffset; const char *fName; BPoint fLastPoint; bool fHasLastPoint; }; ShapeIterator::ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset, const char *name) { CALLED(); fIcon = icon; fShape = to; fPath = NULL; fOffset = offset; fName = name; fLastPoint = offset; fHasLastPoint = false; } status_t ShapeIterator::IterateMoveTo(BPoint *point) { CALLED(); if (fPath) NextPath(); if (!CurrentPath()) return B_ERROR; //fPath->AddPoint(fOffset + *point); fLastPoint = fOffset + *point; fHasLastPoint = true; return B_OK; } status_t ShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePts) { CALLED(); if (!CurrentPath()) return B_ERROR; while (lineCount--) { fPath->AddPoint(fOffset + *linePts); fLastPoint = fOffset + *linePts; fHasLastPoint = true; linePts++; } return B_OK; } status_t ShapeIterator::IterateBezierTo(int32 bezierCount, BPoint *bezierPts) { CALLED(); if (!CurrentPath()) return B_ERROR; BPoint firstPoint(fLastPoint); fLastPoint = fOffset + bezierPts[bezierCount * 3 - 1]; // first point if (firstPoint == fLastPoint) { // combine the first and the last point fPath->AddPoint(firstPoint, fOffset + bezierPts[bezierCount * 3 - 2], fOffset + bezierPts[0], false); // Mark the points as disconnected for now. CleanUp will change this if necessary. fPath->SetClosed(true); } else { fPath->AddPoint(firstPoint, firstPoint, fOffset + bezierPts[0], false); } // middle points for (int i = 1; i + 2 < bezierCount * 3; i += 3) { fPath->AddPoint(fOffset + bezierPts[i + 1], fOffset + bezierPts[i + 0], fOffset + bezierPts[i + 2], false); } // last point if (firstPoint != fLastPoint) { fPath->AddPoint(fLastPoint, fOffset + bezierPts[bezierCount * 3 - 2], fLastPoint, false); } fHasLastPoint = true; return B_OK; } status_t ShapeIterator::IterateClose() { CALLED(); if (!CurrentPath()) return B_ERROR; fPath->SetClosed(true); NextPath(); fHasLastPoint = false; return B_OK; } VectorPath * ShapeIterator::CurrentPath() { CALLED(); if (fPath) return fPath; fPath = new (nothrow) VectorPath(); fPath->SetName(fName); return fPath; } void ShapeIterator::NextPath() { CALLED(); if (fPath) { fPath->CleanUp(); fIcon->Paths()->AddItem(fPath); fShape->Paths()->AddItem(fPath); } fPath = NULL; } // #pragma mark - // constructor StyledTextImporter::StyledTextImporter() : Importer(), fStyleMap(NULL), fStyleCount(0) { CALLED(); } // destructor StyledTextImporter::~StyledTextImporter() { CALLED(); } // Import status_t StyledTextImporter::Import(Icon* icon, BMessage* clipping) { CALLED(); const char *text; ssize_t textLength; if (clipping == NULL) return ENOENT; if (clipping->FindData("text/plain", B_MIME_TYPE, (const void **)&text, &textLength) == B_OK) { text_run_array *runs = NULL; ssize_t runsLength; if (clipping->FindData("application/x-vnd.Be-text_run_array", B_MIME_TYPE, (const void **)&runs, &runsLength) < B_OK) runs = NULL; BString str(text, textLength); return _Import(icon, str.String(), runs); } return ENOENT; } // Import status_t StyledTextImporter::Import(Icon* icon, const entry_ref* ref) { CALLED(); status_t err; BFile file(ref, B_READ_ONLY); err = file.InitCheck(); if (err < B_OK) return err; BNodeInfo info(&file); char mime[B_MIME_TYPE_LENGTH]; err = info.GetType(mime); if (err < B_OK) return err; if (strncmp(mime, "text/plain", B_MIME_TYPE_LENGTH)) return EINVAL; off_t size; err = file.GetSize(&size); if (err < B_OK) return err; if (size > 1 * 1024 * 1024) // Don't load files that big return E2BIG; BMallocIO mio; mio.SetSize((size_t)size + 1); memset((void *)mio.Buffer(), 0, (size_t)size + 1); // TODO: read runs from attribute return _Import(icon, (const char *)mio.Buffer(), NULL); } // _Import status_t StyledTextImporter::_Import(Icon* icon, const char *text, text_run_array *runs) { CALLED(); status_t ret = Init(icon); if (ret < B_OK) { printf("StyledTextImporter::Import() - Init() error: %s\n", strerror(ret)); return ret; } BString str(text); if (str.Length() > 50) { BAlert* alert = new BAlert(B_TRANSLATE("Text too long"), B_TRANSLATE("The text you are trying to import is quite long, " "are you sure?"), B_TRANSLATE("Import text"), B_TRANSLATE("Cancel"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); if (alert->Go()) return B_CANCELED; } // import run colors as styles if (runs) { delete[] fStyleMap; fStyleMap = new struct style_map[runs->count]; for (int32 i = 0; runs && i < runs->count; i++) { _AddStyle(icon, &runs->runs[i]); } } int32 currentRun = 0; text_run *run = NULL; if (runs) run = &runs->runs[0]; int32 len = str.Length(); int32 chars = str.CountChars(); BPoint origin(0, 0); BPoint offset(origin); for (int32 i = 0, c = 0; i < len && c < chars; c++) { // make sure we are still on the (good) run while (run && currentRun < runs->count - 1 && i >= runs->runs[currentRun + 1].offset) { run = &runs->runs[++currentRun]; //printf("switching to run %d\n", currentRun); } int charLen; for (charLen = 1; str.ByteAt(i + charLen) & 0x80; charLen++); BShape glyph; BShape *glyphs[1] = { &glyph }; BFont font(be_plain_font); if (run) font = run->font; // first char if (offset == BPoint(0,0)) { font_height height; font.GetHeight(&height); origin.y += height.ascent; offset = origin; } // LF if (str[i] == '\n') { // XXX: should take the MAX() for the line // XXX: should use descent + leading from previous line font_height height; font.GetHeight(&height); origin.y += height.ascent + height.descent + height.leading; offset = origin; i++; continue; } float charWidth; charWidth = font.StringWidth(str.String() + i, charLen); //printf("StringWidth( %d) = %f\n", charLen, charWidth); BString glyphName(str.String() + i, charLen); glyphName.Prepend("Glyph ("); glyphName.Append(")"); font.GetGlyphShapes((str.String() + i), 1, glyphs); if (glyph.Bounds().IsValid()) { //offset.x += glyph.Bounds().Width(); offset.x += charWidth; PathSourceShape* shape = new (nothrow) PathSourceShape(NULL); if (shape == NULL) return B_NO_MEMORY; shape->SetName(glyphName.String()); if (!icon->Shapes()->AddItem(shape)) { delete shape; return B_NO_MEMORY; } for (int j = 0; run && j < fStyleCount; j++) { if (fStyleMap[j].run == run) { shape->SetStyle(fStyleMap[j].style); break; } } ShapeIterator iterator(icon, shape, offset, glyphName.String()); if (iterator.Iterate(&glyph) < B_OK) return B_ERROR; } // skip the rest of UTF-8 char bytes for (i++; i < len && str[i] & 0x80; i++); } delete[] fStyleMap; fStyleMap = NULL; return B_OK; } // #pragma mark - // _AddStyle status_t StyledTextImporter::_AddStyle(Icon *icon, text_run *run) { CALLED(); if (!run) return EINVAL; rgb_color color = run->color; Style* style = new(std::nothrow) Style(color); if (style == NULL) return B_NO_MEMORY; char name[30]; sprintf(name, B_TRANSLATE_COMMENT("Color (#%02x%02x%02x)", "Style name after dropping a color"), color.red, color.green, color.blue); style->SetName(name); bool found = false; for (int i = 0; i < fStyleCount; i++) { if (*style == *(fStyleMap[i].style)) { delete style; style = fStyleMap[i].style; found = true; break; } } if (!found && !icon->Styles()->AddItem(style)) { delete style; return B_NO_MEMORY; } fStyleMap[fStyleCount].run = run; fStyleMap[fStyleCount].style = style; fStyleCount++; return B_OK; } // _AddPaths status_t StyledTextImporter::_AddPaths(Icon *icon, BShape *shape) { CALLED(); return B_ERROR; } // _AddShape status_t StyledTextImporter::_AddShape(Icon *icon, BShape *shape, text_run *run) { CALLED(); return B_ERROR; }