examples/activex_cpp/text_formatting/text_formatting.cpp
119 lines
// ============================================================================
// text_formatting -- C++ ActiveX (COM) port, following
// examples/activex/text_formatting/text_formatting.vbs
//
// Lays sample.txt out into three justified COLUMNS.
//
// The layout is driven entirely by the page-break callback: WriteFText fills
// the current text rectangle, and when it runs out the engine asks what to do
// next. The handler either moves the rectangle to the next column (same page)
// or closes the page and starts a new one. That is the whole multi-column
// algorithm -- there is no second pass.
//
// CreateNewPDF RESETS the page-break proc in the engine, so SetOnPageBreakProc
// has to be (re)armed AFTER it, not before.
// ============================================================================
#import "..\\..\\wrappers\\activex\\LumasPdfAX.tlb" no_namespace named_guids
#include <windows.h>
#include "axcommon.h"
#include <cstdio>
#include <string>
// Output-rectangle state shared with the page-break event.
static ILumasPDFPtr pdf;
static double gPosX, gPosY, gWidth, gHeight, gDistance;
static int gColumn, gColCount;
class BreakSink : public AxSink {
public:
long OnError(long, const wchar_t*, long) override { return 0; }
// Places the next column or starts a new page.
long OnPageBreak(double, double, bool pageBreak) override {
pdf->SetPageCoords(pcTopDown); // we use top down coordinates
++gColumn;
// pageBreak is true if the string contains an explicit page-break tag.
if (!pageBreak && gColumn < gColCount) {
// change the output rectangle to the next column, do NOT close the page
double x = gPosX + gColumn * (gWidth + gDistance);
pdf->SetTextRect(x, gPosY, gWidth, gHeight);
} else {
// the page is full: close it and append a new one
pdf->EndPage();
pdf->Append();
pdf->SetTextRect(gPosX, gPosY, gWidth, gHeight);
gColumn = 0;
}
return 0; // we do not change the alignment
}
};
static std::string ReadAll(const std::string& path) {
std::string out;
FILE* f = fopen(path.c_str(), "rb");
if (!f) return out;
char buf[8192];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), f)) > 0) out.append(buf, n);
fclose(f);
return out;
}
int main() {
ChdirToExe();
ComInit com;
if (!com.ok()) return 1;
// Stage sample.txt next to the exe (from the shared test_files folder).
std::string sample = OutFile("sample.txt");
if (ReadAll(sample).empty())
CopyFileA("E:\\LUMASPDFSDK\\examples\\test_files\\sample.txt", sample.c_str(), FALSE);
std::string fText = ReadAll(sample);
if (FAILED(pdf.CreateInstance(L"LumasPdf.PDF"))) return 1;
BreakSink sink;
sink.Advise(pdf);
pdf->RaiseExceptions = VARIANT_FALSE;
pdf->SetDocInfoW(diCreator, B("LumasPdf ActiveX test app"));
pdf->SetDocInfoW(diSubject, B("Multi-column text"));
pdf->SetDocInfoW(diTitle, B("Multi-column text"));
pdf->SetPageCoords(pcTopDown);
if (!pdf->CreateNewPDFW(B(""))) { std::printf("CreateNewPDF failed\n"); return 1; }
// CreateNewPDF resets the page-break proc in the engine, so (re)arm it now.
pdf->SetOnPageBreakProc();
// Initialize the output rectangle, number of columns and so on.
gColCount = 3; // the VB6 form combo default was 3 columns
gColumn = 0;
gDistance = 10;
gPosX = 50;
gPosY = 50;
pdf->Append(); // append a new page
gHeight = pdf->GetPageHeight() - 100;
gWidth = (pdf->GetPageWidth() - 100 - (gColCount - 1) * gDistance) / gColCount;
pdf->SetTextRect(gPosX, gPosY, gWidth, gHeight);
pdf->SetFontW(B("Arial"), fsNone, 9, VARIANT_TRUE, cp1252); // a font is always required
pdf->WriteFTextW(taJustify, B(fText)); // fires OnPageBreak
pdf->EndPage(); // close the last page
int rc = 0;
if (pdf->HaveOpenDoc()) {
std::string outFile = OutFile();
if (!pdf->OpenOutputFileW(B(outFile))) {
std::printf("OpenOutputFile failed\n");
rc = 1;
} else if (pdf->CloseFile()) {
std::printf("OK: %s\n", outFile.c_str());
}
}
sink.Unadvise();
pdf = nullptr;
return rc;
}
This file is in the SDK at examples/activex_cpp/text_formatting/text_formatting.cpp.
The build fails if this page and that file ever differ.