1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "print_cups_ppd.h"
17 #include "print_service_converter.h"
18 #include "print_log.h"
19
20
21 namespace OHOS::Print {
22 using namespace std;
23
24 const std::vector<std::string> baseOptionStr = {
25 "PageSize",
26 "PageRegion",
27 "MediaType",
28 "ColorModel",
29 "OutputMode",
30 "cupsPrintQuality",
31 "Duplex",
32 "JCLDuplex",
33 "EFDuplex",
34 "EFDuplexing",
35 "KD03Duplex",
36 "Collate",
37 "OutputOrder",
38 "CNGrayscale",
39 "BRMonoColor"
40 };
41
GetCNFromPpdAttr(ppd_file_t * ppd,const char * keyword,const char * choice,const char * defaultText)42 const char *GetCNFromPpdAttr(ppd_file_t *ppd, const char *keyword, const char* choice, const char* defaultText)
43 {
44 ppd_attr_t *locattrCN = _ppdLocalizedAttr(ppd, keyword, choice, "zh_CN");
45 if (!locattrCN) {
46 return defaultText;
47 }
48
49 return locattrCN->text;
50 }
51
GetAdvanceOptJsSingleJSFromOption(ppd_file_t * ppd,ppd_option_t * opt,Json::Value & advanceOptJsSingle)52 void GetAdvanceOptJsSingleJSFromOption(ppd_file_t *ppd, ppd_option_t *opt, Json::Value& advanceOptJsSingle)
53 {
54 Json::Value advanceChoiceJs;
55 Json::Value advanceChoiceJsDefaultLanguage;
56 Json::Value advanceChoiceJsCNLanguage;
57 Json::Value advanceOptionTextJs;
58
59 PRINT_HILOGI("option=%{public}s default=%{public}s text=%{public}s\n", opt->keyword, opt->defchoice, opt->text);
60 ppd_choice_t* choices = opt->choices;
61 for (int k = 0; k < opt->num_choices; k++) {
62 if (choices + k == nullptr) {
63 PRINT_HILOGE("PPD choice found error: %{public}s", opt->keyword);
64 break;
65 }
66 if (!strcmp(choices[k].choice, "Custom") && ppdFindCustomOption(ppd, opt->keyword)) {
67 PRINT_HILOGI("Ignore Custom PPD Choice: %{public}s", opt->keyword);
68 continue;
69 }
70 advanceChoiceJsDefaultLanguage[choices[k].choice] = choices[k].text;
71 advanceChoiceJsCNLanguage[choices[k].choice] = GetCNFromPpdAttr(ppd, opt->keyword, choices[k].choice,
72 choices[k].text);
73 }
74 advanceChoiceJs["default"] = advanceChoiceJsDefaultLanguage;
75 advanceChoiceJs["zh_CN"] = advanceChoiceJsCNLanguage;
76 advanceOptionTextJs["default"] = opt->text;
77 advanceOptionTextJs["zh_CN"] = GetCNFromPpdAttr(ppd, "Translation", opt->keyword, opt->text);
78 advanceOptJsSingle["choice"] = advanceChoiceJs;
79 advanceOptJsSingle["keyword"] = opt->keyword;
80 advanceOptJsSingle["optionText"] = advanceOptionTextJs;
81 advanceOptJsSingle["uiType"] = opt->ui;
82 }
83
GetAdvanceOptionsFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)84 void GetAdvanceOptionsFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
85 {
86 cups_array_t *options_arr = ppd->options;
87 Json::Value advanceOptJs;
88 Json::Value advanceDefaultJs;
89 for (ppd_option_t *opt = (ppd_option_t*)cupsArrayFirst(options_arr);
90 opt != nullptr; opt = (ppd_option_t*)cupsArrayNext(options_arr)) {
91 if (advanceOptJs.size() > ADVANCE_OPTION_MAXLENGTH) {
92 PRINT_HILOGE("AdvanceOption size is over max length!");
93 break;
94 }
95 if (std::find(baseOptionStr.begin(), baseOptionStr.end(), string(opt->keyword)) != baseOptionStr.end()) {
96 continue;
97 }
98 if (opt->ui == PPD_UI_PICKMANY) {
99 PRINT_HILOGW("PPD_UI_PICKMANY Found: %{public}s", opt->keyword);
100 continue;
101 }
102 advanceDefaultJs[opt->keyword] = opt->defchoice;
103 Json::Value advanceOptJsSingle;
104 GetAdvanceOptJsSingleJSFromOption(ppd, opt, advanceOptJsSingle);
105 advanceOptJs.append(advanceOptJsSingle);
106 }
107 printerCaps.SetPrinterAttrNameAndValue("advanceOptions", PrintJsonUtil::WriteString(advanceOptJs).c_str());
108 printerCaps.SetPrinterAttrNameAndValue("advanceDefault", PrintJsonUtil::WriteString(advanceDefaultJs).c_str());
109 }
110
QueryPrinterCapabilityFromPPDFile(PrinterCapability & printerCaps,const std::string & ppdFilePath)111 int32_t QueryPrinterCapabilityFromPPDFile(PrinterCapability &printerCaps, const std::string &ppdFilePath)
112 {
113 PRINT_HILOGI("QueryPrinterCapabilityFromPPDFile start %{private}s", ppdFilePath.c_str());
114 char *locale = setlocale(LC_ALL, "zh_CN.UTF-8");
115 if (locale == nullptr) {
116 PRINT_HILOGE("setlocale fail");
117 return E_PRINT_FILE_IO;
118 }
119 ppd_file_t *ppd = ppdOpenFile(ppdFilePath.c_str());
120 if (ppd == nullptr) {
121 PRINT_HILOGE("Open PPD File fail");
122 return E_PRINT_FILE_IO;
123 }
124
125 ppd->cache = _ppdCacheCreateWithPPD(ppd);
126 if (ppd->cache == nullptr) {
127 PRINT_HILOGE("PPDCacheCreateWithPPD fail");
128 return E_PRINT_FILE_IO;
129 }
130 GetAdvanceOptionsFromPPD(ppd, printerCaps);
131 ParsePrinterAttributesFromPPD(ppd, printerCaps);
132
133 ppdClose(ppd);
134 return E_PRINT_NONE;
135 }
136
ParsePageSizeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)137 void ParsePageSizeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
138 {
139 Json::Value mediaSizeMap;
140 Json::Value mediaSizeMapDefaultLanguage;
141 Json::Value mediaSizeMapCNLanguage;
142 _ppd_cache_t *ppdCache = ppd->cache;
143 pwg_size_t *cacheSizes = ppdCache->sizes;
144 if (cacheSizes == nullptr) {
145 PRINT_HILOGE("cacheSizes get nullptr!");
146 return;
147 }
148 std::vector<PrintPageSize> supportedPageSizes;
149 ppd_option_t *sizeOption = ppdFindOption(ppd, "PageSize");
150
151 for (int i = 0; i < ppdCache->num_sizes; i++) {
152 pwg_size_t pwgSize = cacheSizes[i];
153 ppd_choice_t *sizeChoice = ppdFindChoice(sizeOption, pwgSize.map.ppd);
154 if (sizeChoice == nullptr) {
155 PRINT_HILOGE("sizeChoice get nullptr!");
156 return;
157 }
158 std::string id = PrintPageSize::MatchPageSize(pwgSize.map.pwg);
159 PrintPageSize dst;
160 if (!id.empty()) {
161 PrintPageSize::FindPageSizeById(id, dst);
162 } else {
163 dst = PrintPageSize(sizeChoice->text, pwgSize.map.pwg,
164 round(pwgSize.width * HUNDRED_OF_MILLIMETRE_TO_INCH / ONE_HUNDRED),
165 round(pwgSize.length * HUNDRED_OF_MILLIMETRE_TO_INCH / ONE_HUNDRED));
166 }
167 supportedPageSizes.emplace_back(dst);
168
169 mediaSizeMapDefaultLanguage[pwgSize.map.pwg] = sizeChoice->text;
170 mediaSizeMapCNLanguage[pwgSize.map.pwg] = GetCNFromPpdAttr(ppd, "PageSize",
171 sizeChoice->choice, sizeChoice->text);
172 }
173 mediaSizeMap["default"] = mediaSizeMapDefaultLanguage;
174 mediaSizeMap["zh_CN"] = mediaSizeMapCNLanguage;
175 printerCaps.SetSupportedPageSize(supportedPageSizes);
176 printerCaps.SetPrinterAttrNameAndValue("mediaSizeMap", PrintJsonUtil::WriteString(mediaSizeMap).c_str());
177
178 // find default media size
179 if (sizeOption && sizeOption->defchoice) {
180 pwg_size_t *pwgSize = _ppdCacheGetSize(ppdCache, sizeOption->defchoice);
181 PRINT_HILOGI("Default page size: %{public}s, defchoice : %{public}s", pwgSize->map.ppd, sizeOption->defchoice);
182 std::string defaultPageSizeId;
183 if (!ConvertPageSizeId(pwgSize->map.pwg, defaultPageSizeId)) {
184 defaultPageSizeId = std::string(pwgSize->map.ppd);
185 }
186 printerCaps.SetPrinterAttrNameAndValue("defaultPageSizeId", defaultPageSizeId.c_str());
187 } else {
188 PRINT_HILOGE("Default page size: None");
189 }
190 }
191
ParseDuplexModeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)192 void ParseDuplexModeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
193 {
194 _ppd_cache_t *ppdCache = ppd->cache;
195 std::vector<DuplexModeCode> supportedDuplexModeList = { DUPLEX_MODE_ONE_SIDED };
196 if (ppdCache->sides_2sided_long) {
197 supportedDuplexModeList.emplace_back(DUPLEX_MODE_TWO_SIDED_LONG_EDGE);
198 }
199 if (ppdCache->sides_2sided_short) {
200 supportedDuplexModeList.emplace_back(DUPLEX_MODE_TWO_SIDED_SHORT_EDGE);
201 }
202 printerCaps.SetSupportedDuplexMode(std::vector<uint32_t>(supportedDuplexModeList.begin(),
203 supportedDuplexModeList.end()));
204 if (static_cast<int>(supportedDuplexModeList.size()) <= 1) {
205 printerCaps.SetDuplexMode((uint32_t)DUPLEX_MODE_ONE_SIDED);
206 } else {
207 printerCaps.SetDuplexMode((uint32_t)DUPLEX_MODE_TWO_SIDED_LONG_EDGE);
208 }
209 std::string duplexModeJson = ConvertListToJson<DuplexModeCode>(supportedDuplexModeList, ConvertDuplexModeToJson);
210 printerCaps.SetPrinterAttrNameAndValue("sides-supported", duplexModeJson.c_str());
211 // find default duplex mode
212 ppd_option_t *duplex = nullptr;
213 DuplexModeCode code = DUPLEX_MODE_ONE_SIDED;
214 const std::vector<std::string> duplexOptions = { "Duplex", "EFDuplex", "EFDuplexing", "KD03Duplex", "JCLDuplex" };
215 for (const auto& option : duplexOptions) {
216 duplex = ppdFindOption(ppd, option.c_str());
217 if (duplex != nullptr) {
218 break;
219 }
220 }
221 if (duplex && duplex->num_choices > 1) {
222 if (!strcmp(duplex->defchoice, "DuplexTumble"))
223 code = DUPLEX_MODE_TWO_SIDED_SHORT_EDGE;
224 else if (!strcmp(duplex->defchoice, "DuplexNoTumble"))
225 code = DUPLEX_MODE_TWO_SIDED_LONG_EDGE;
226 }
227 uint32_t value = static_cast<uint32_t>(code);
228 printerCaps.SetPrinterAttrNameAndValue("sides-default", std::to_string(value).c_str());
229 }
230
ParseMediaTypeAttributeFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)231 void ParseMediaTypeAttributeFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
232 {
233 ppd_option_t *typeOption = ppdFindOption(ppd, "MediaType");
234 if (typeOption == nullptr) {
235 PRINT_HILOGE("ppdFindOption MediaType fail!");
236 return;
237 }
238 _ppd_cache_t *ppdCache = ppd->cache;
239 Json::Value jsonArray;
240 Json::Value jsonArrayDefaultLanguage;
241 Json::Value jsonArrayCNLanguage;
242
243 // compatible with Spooler USB
244 Json::Value jsonArrayOld;
245
246 std::vector<std::string> supportedMediaTypeList;
247 for (int i = 0; i < ppdCache->num_types; i++) {
248 if (ppdCache->types + i == nullptr) {
249 PRINT_HILOGE("ppdCache types is nullptr!");
250 return;
251 }
252 ppd_choice_t *typeChoice = ppdFindChoice(typeOption, ppdCache->types[i].ppd);
253 if (typeChoice == nullptr) {
254 PRINT_HILOGE("typeChoice is nullptr!");
255 return;
256 }
257 jsonArrayDefaultLanguage[ppdCache->types[i].pwg] = typeChoice->text;
258 jsonArrayCNLanguage[ppdCache->types[i].pwg] = GetCNFromPpdAttr(ppd, "MediaType",
259 typeChoice->choice, typeChoice->text);
260 // compatible with Spooler USB
261 jsonArrayOld.append(ppdCache->types[i].pwg);
262
263 supportedMediaTypeList.emplace_back(std::string(ppdCache->types[i].pwg));
264 }
265 jsonArray["default"] = jsonArrayDefaultLanguage;
266 jsonArray["zh_CN"] = jsonArrayCNLanguage;
267 printerCaps.SetSupportedMediaType(supportedMediaTypeList);
268
269 if (!supportedMediaTypeList.empty()) {
270 printerCaps.SetPrinterAttrNameAndValue("mediaTypeMap", PrintJsonUtil::WriteString(jsonArray).c_str());
271 // compatible with Spooler USB
272 printerCaps.SetPrinterAttrNameAndValue("media-type-supported",
273 PrintJsonUtil::WriteString(jsonArrayOld).c_str());
274 }
275
276 if (typeOption->defchoice == nullptr) {
277 PRINT_HILOGE("typeOption->defchoice fail!");
278 return;
279 }
280 const char *typeDefault = _ppdCacheGetType(ppdCache, typeOption->defchoice);
281 if (typeDefault == nullptr) {
282 PRINT_HILOGE("_ppdCacheGetType typeDefault fail! typeOption: %{public}s", typeOption->defchoice);
283 return;
284 }
285 printerCaps.SetPrinterAttrNameAndValue("media-type-default", typeDefault);
286 }
287
ParseColorModeAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)288 void ParseColorModeAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
289 {
290 std::vector<ColorModeCode> supportedColorModes = { ColorModeCode::COLOR_MODE_MONOCHROME };
291 ColorModeCode defaultColor = ColorModeCode::COLOR_MODE_MONOCHROME;
292 printerCaps.SetColorMode(ColorModeCode::COLOR_MODE_MONOCHROME);
293 if (ppd->color_device) {
294 printerCaps.SetColorMode(ColorModeCode::COLOR_MODE_COLOR);
295 supportedColorModes.emplace_back(ColorModeCode::COLOR_MODE_COLOR);
296 defaultColor = ColorModeCode::COLOR_MODE_COLOR;
297 }
298 printerCaps.SetSupportedColorMode(std::vector<uint32_t>(supportedColorModes.begin(), supportedColorModes.end()));
299
300 std::string keyword = "print-color-mode-supported";
301 std::string colorModeJson = ConvertListToJson<ColorModeCode>(supportedColorModes, ConvertColorModeToJson);
302 printerCaps.SetPrinterAttrNameAndValue(keyword.c_str(), colorModeJson.c_str());
303
304 uint32_t mode = static_cast<uint32_t>(defaultColor);
305 printerCaps.SetPrinterAttrNameAndValue("defaultColorMode", std::to_string(mode).c_str());
306 }
307
308
ParseQualityAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)309 void ParseQualityAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
310 {
311 std::vector<PrintQualityCode> supportedQualitiesList;
312 ppd_option_t *outputMode = ppdFindOption(ppd, "OutputMode");
313 if (!outputMode) {
314 outputMode = ppdFindOption(ppd, "cupsPrintQuality");
315 }
316 if (outputMode) {
317 if (ppdFindChoice(outputMode, "draft") || ppdFindChoice(outputMode, "fast")) {
318 supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_DRAFT);
319 }
320 supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_NORMAL);
321 if (ppdFindChoice(outputMode, "best") || ppdFindChoice(outputMode, "high")) {
322 supportedQualitiesList.emplace_back(PrintQualityCode::PRINT_QUALITY_HIGH);
323 }
324 } else {
325 supportedQualitiesList = { PrintQualityCode::PRINT_QUALITY_NORMAL };
326 };
327
328 printerCaps.SetSupportedQuality(std::vector<uint32_t>(supportedQualitiesList.begin(),
329 supportedQualitiesList.end()));
330
331 // compatible with Spooler USB
332 Json::Value supportedQualities;
333 for (auto &quality : supportedQualitiesList) {
334 Json::Value jsonObject;
335 uint32_t value = static_cast<uint32_t>(quality);
336 jsonObject["quality"] = value;
337 supportedQualities.append(jsonObject);
338 }
339 std::string attrString = PrintJsonUtil::WriteString(supportedQualities);
340 std::string keyword = "print-quality-supported";
341 printerCaps.SetPrinterAttrNameAndValue(keyword.c_str(), attrString.c_str());
342 }
343
SetOptionAttributeFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)344 void SetOptionAttributeFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
345 {
346 Json::Value options;
347 if (ppd->nickname) {
348 options["make"] = ppd->nickname;
349 } else if (ppd->modelname) {
350 options["make"] = ppd->modelname;
351 } else {
352 options["make"] = "Bad PPD File";
353 }
354
355 Json::Value cupsOptionsJson = printerCaps.GetPrinterAttrGroupJson();
356 options["cupsOptions"] = cupsOptionsJson;
357
358 std::string optionStr = PrintJsonUtil::WriteString(options);
359 PRINT_HILOGD("SetOption: %{public}s", optionStr.c_str());
360 printerCaps.SetOption(optionStr);
361 }
362
ParsePrinterAttributesFromPPD(ppd_file_t * ppd,PrinterCapability & printerCaps)363 void ParsePrinterAttributesFromPPD(ppd_file_t *ppd, PrinterCapability &printerCaps)
364 {
365 ParseColorModeAttributesFromPPD(ppd, printerCaps);
366 ParseDuplexModeAttributesFromPPD(ppd, printerCaps);
367 ParsePageSizeAttributesFromPPD(ppd, printerCaps);
368 ParseQualityAttributesFromPPD(ppd, printerCaps);
369 ParseMediaTypeAttributeFromPPD(ppd, printerCaps);
370 SetOptionAttributeFromPPD(ppd, printerCaps);
371 }
372
373 }